博客
关于我
[NOIP2018]普及组题解
阅读量:339 次
发布时间:2019-03-04

本文共 5680 字,大约阅读时间需要 18 分钟。

有好久都没写博客呢了。。。

标题统计

签到题

#include 
#include
#include
#include
using namespace std;char a;int ans;int main() { // freopen("title.in", "r", stdin);// freopen("title.out", "w", stdout); while((scanf("%c", &a)) != EOF) { if((a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || (a >= '0' && a <= '9')) ans ++; } printf("%d", ans); return 0;}

龙虎斗

模拟题,细节很多。先跑一遍循环算出龙方和虎方的气势,再暴力枚举要派去工兵的兵营,统计气势差最大值即可。

#include 
#include
#include
#include
#define LL long longusing namespace std;const int MAXN = 1e5 + 5;const LL lox = 9223372036854775807;int n, m, p, s, t, Num;LL ans1, ans2, minn, Q, a[MAXN];LL Abs_(LL x) { return x > 0 ? x : -x;}int main() { // freopen("fight.in", "r", stdin);// freopen("fight.out", "w", stdout); scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%lld", &a[i]); } scanf("%d%d%d%d", &m, &p, &s, &t); a[p] += s; minn = lox; for(int i = 1; i <= n; i ++) { if(i < m) { ans1 += a[i] * (m - i); } else if(i > m) { ans2 += a[i] * (i - m); } } for(int i = 1; i <= n; i ++) { if(i < m) { Q = (LL)t * (m - i); if((Abs_(ans1 + Q - ans2)) < minn) { minn = Abs_(ans1 + Q - ans2); Num = i; } } else if(i > m) { Q = (LL)t * (i - m); if((Abs_(Q + ans2 - ans1)) < minn) { minn = Abs_(Q + ans2 - ans1); Num = i; } } else { if((Abs_(ans1 - ans2)) < minn) { minn = Abs_(ans1 - ans2); Num = i; } } } printf("%d", Num); return 0;}

摆渡车

d p dp dp

这道题还挺有难度的。
初看题以为是贪心,仔细想了一会儿后发现贪心做不了,于是改成了 d p dp dp,然后,,,就WA了。
我认为做这种题不能光看表面,要挖掘题的模型。就比如这道题,题意就可以翻译成:求将时间轴分成任意个区间,使各个区间长度不小于 m m m,各个点到它属于区间的右端点距离的和的最小值。
d p [ i ] dp[i] dp[i]等于上面那一堆东西。
于是就不难推出 d p [ i ] = min ⁡ j ≤ i − m { d p ( j ) + ∑ j < a [ k ] ≤ i { i − a [ k ] } } dp[i]=\min \limits _ { j \leq { i - m} } \{dp(j) + \sum \limits _ {j<a[k]\leq i} \{i-a[k]\}\} dp[i]=jimmin{
dp(j)+
j<a[k]i{
i
a[k]}}
又因为 ∑ { i − a [ k ] } = ∑ { i } − ∑ { a [ k ] } \sum\{i - a[k]\}=\sum\{i\}-\sum\{a[k]\} {
i
a[k]}={
i}
{
a[k]}
即满足 j < a [ k ] ≤ i j < a[k] \leq i j<a[k]i条件的 k k k的个数 × i   − \times i\ - ×i  k k k个点的位置和。
于是令 p r e [ i ] = ( − ∞ , i ] pre[i]=(-\infty, i] pre[i]=(,i]中 点的位置和, c n t [ i ] = ( − ∞ , i ] cnt[i]=(-\infty, i] cnt[i]=(,i]中的点数。
d p [ i ] = min ⁡ j ≤ i − m { d p ( j ) − ( p r e [ i ] − p r e [ j ] ) + ( c n t [ i ] − c n t [ j ] ) × i } dp[i]=\min \limits _ { j \leq { i - m} } \{dp(j) - (pre[i] - pre[j]) + (cnt[i] - cnt[j])\times i\} dp[i]=jimmin{
dp(j)
(pre[i]pre[j])+(cnt[i]cnt[j])×i}
相信读者很容易就能想到,最后的答案
a n s = max ⁡ m a x x ≤ i ≤ m a x x + m − 1 { d p [ i ] } ans = \max \limits_{maxx \leq i \leq {maxx + m - 1}}\{dp[i]\} ans=maxximaxx+m1max{
dp[i]}
其中 m a x x maxx maxx为所有 a [ i ] a[i] a[i]的最大值。
此时时间复杂度 O ( T 2 ) O(T^2) O(T2)
这样交上去是拿不了满分的。于是我们考虑优化:
1.将状态转移方程改为 d p [ i ] = min ⁡ i − 2 × m ≤ j ≤ i − m { d p ( j ) − ( p r e [ i ] − p r e [ j ] ) + ( c n t [ i ] − c n t [ j ] ) × i } dp[i]=\min \limits _ {
{i - 2 \times m}\leq j \leq { i - m} } \{dp(j) - (pre[i] - pre[j]) + (cnt[i] - cnt[j])\times i\}
dp[i]=i2×mjimmin{
dp(j)
(pre[i]pre[j])+(cnt[i]cnt[j])×i}
为什么呢?你想,当区间长度大于了 2 × m 2\times m 2×m,可以把这个大区间分成很多个小区间,答案一定不会比原来更差,所以此优化正确。
2.若 c n t [ i ] = c n t [ i − m ] cnt[i]=cnt[i-m] cnt[i]=cnt[im],即此长度为 m m m的区间中没有一个点,那 d p [ i ] = d p [ i − m ] dp[i]=dp[i-m] dp[i]=dp[im],就不用去跑第二重循环了。
由于优化2使进入第二重循环的次数不超过 m × n m\times n m×n次,优化1使第二重循环的时间复杂度降为了 m m m。所以总时间复杂度: O ( n m 2 + T ) O(n m^2+T) O(nm2+T)
听说还有更厉害的优化方法,但是我不会…

#include 
#include
#include
#include
using namespace std;const int MAXN = 505, MAXX = 4 * 1e6 + 110, inx = 2147483647;int n, m, a[MAXN], maxx = -inx, C[MAXX], dp[MAXX], pre[MAXX], cnt[MAXX], Min = inx;int main() { // freopen("bus.in", "r", stdin);// freopen("bus.out", "w", stdout); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); pre[a[i]] += a[i]; cnt[a[i]] ++; maxx = max(maxx, a[i]); } for(int i = 1; i < maxx + m; i ++) { pre[i] += pre[i - 1]; cnt[i] += cnt[i - 1]; } for(int i = 0; i < maxx + m; i ++) { if(i >= m && cnt[i] == cnt[i - m]) { dp[i] = dp[i - m]; continue; } dp[i] = cnt[i] * i - pre[i]; for(int j = max(i - 2 * m + 1, 0); j <= i - m; j ++) { dp[i] = min(dp[i], dp[j] - (pre[i] - pre[j]) + (cnt[i] - cnt[j]) * i); } } for(int i = maxx; i < maxx + m; i ++) { Min = min(Min, dp[i]); } printf("%d", Min); return 0;}

对称二叉树

暴力树Hash

作为T4,竟然能被暴力水过。。。

法一:暴力枚举

对的,你没看错,就是暴力。

#include 
#include
#include
#include
#include
#define uLL unsigned long longusing namespace std;const int MAXN = 1e6 + 5;bool vis[MAXN];int a[MAXN], n, zs[MAXN], maxx = 1;struct Node { int L_C, R_C;}arr[MAXN];int dfs(int Num) { if(zs[Num]) return zs[Num]; if(arr[Num].L_C != -1) zs[Num] += dfs(arr[Num].L_C); if(arr[Num].R_C != -1) zs[Num] += dfs(arr[Num].R_C); zs[Num] ++; return zs[Num];}bool check(int x, int y) { if(x == -1 && y == -1) return 1; if(x != -1 && y != -1 && a[x] == a[y] && check(arr[x].L_C, arr[y].R_C) && check(arr[x].R_C, arr[y].L_C)) return 1; return 0;}int main() { // freopen("tree.in", "r", stdin);// freopen("tree.out", "w", stdout); scanf("%d", &n); for(int i = 1; i <= n; i ++) { scanf("%d", &a[i]); } for(int i = 1; i <= n; i ++) { scanf("%d%d", &arr[i].L_C, &arr[i].R_C); } zs[1] = dfs(1); for(int i = 1; i <= n; i ++) { if(check(arr[i].L_C, arr[i].R_C)) { maxx = max(maxx, zs[i]); } } printf("%d", maxx); return 0;}

此算法在树为完全二叉树时运行最慢(因为其他情况可以剪掉很多枝),时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

法二:树Hash

先咕着,明天更,,,

转载地址:http://lxve.baihongyu.com/

你可能感兴趣的文章
脱壳与加壳-加壳-6-代码实现加密导入表
查看>>
Typora配置PicGo时,提示Failed to fetch
查看>>
Windows 7 安装 .NET 5 / .NET Core 3.1 环境的方法和依赖文件
查看>>
ASP.NET Core 使用 URL Rewrite 中间件实现 HTTP 重定向到 HTTPS
查看>>
ASP.NET CORE MVC 实现减号分隔(Kebab case)样式的 URL
查看>>
SQL优化 MySQL版 -分析explain SQL执行计划与笛卡尔积
查看>>
bcolz的新操作
查看>>
Linux的s、t、i、a权限(转)
查看>>
zmq的send
查看>>
C++中的delete加深认识
查看>>
windows消息机制(转)
查看>>
STL笔试面试题总结(干货)(转)
查看>>
XML 和 HTML 之间的差异
查看>>
qt中moc的作用
查看>>
阿里钉钉面试题
查看>>
华为社招笔试
查看>>
VS环境变量(转)
查看>>
C++中找资源或者函数的方法
查看>>
一些留给自己的思考题(只求回过头来能够有所获)
查看>>
SQL函数返回表的写法
查看>>