【BZOJ3675】序列统计,斜率优化DP
2016-04-22 21:05
274 查看
传送门
写在前面:停课的开始
思路:
这是一道有点丧病的斜率DP。
首先发现,如果要切位置i和j,那么先切i和先切j得到的是同样的结果(只是想着应该是这样,用暴力试了一下,但太弱不会证明)
我的DP方程好像和网上的不太一样
f[i][j]是指在第j个数的前面”砍“第i下的最大值(即把第j个数当作第i段的末尾)那么转移方程就显而易见了
f[i][j]=max(f[i−1][p]+(sum[n]−sum[j])∗(sum[j]−sum[p]))p=0..j
最终答案是max(f[k][i])其中i=1..n−1
(sum[x]=∑xi=1a[i])
设x>y且x优于y的转移,则
(f[x]−f[y])/(sum[x]−sum[y])>sum[n]−sum[j]
而且计算100000*200*8/1024/1024≈152,空间爆炸,所以开滚动数组,队列记录上一次的转移,head和tail也要做些小处理,答案用long long计算
然后就过了样例,欢天喜地地交上去了
不出4ms就WA了……
又试了一组数据,发现和暴力结果不同
os:好像数据中0比较多?
结果看了眼std发现要特判0?
os:???
想了好久发现判断斜率时的sum[x]−sum[y]可能会因为a[x]=0而除0出错(除以实数0会爆出一些奇怪的东西(‵~′))……
所以要判0啊!判0啊!
附上文所说的证明(转自DaD3zZ):
注意:判0啊!
代码:
冷静下来,重新看看题目,其实和之前的斜率DP没什么两样,但也许是觉得太模式化了,连最基本的除0错误都忽略掉了,不得不说是对于细节的把握力不够而导致的,也许再细心一些,就能在不通过外力的情况下找出这种小错误了
写在前面:停课的开始
思路:
这是一道有点丧病的斜率DP。
首先发现,如果要切位置i和j,那么先切i和先切j得到的是同样的结果(只是想着应该是这样,用暴力试了一下,但太弱不会证明)
我的DP方程好像和网上的不太一样
f[i][j]是指在第j个数的前面”砍“第i下的最大值(即把第j个数当作第i段的末尾)那么转移方程就显而易见了
f[i][j]=max(f[i−1][p]+(sum[n]−sum[j])∗(sum[j]−sum[p]))p=0..j
最终答案是max(f[k][i])其中i=1..n−1
(sum[x]=∑xi=1a[i])
设x>y且x优于y的转移,则
(f[x]−f[y])/(sum[x]−sum[y])>sum[n]−sum[j]
而且计算100000*200*8/1024/1024≈152,空间爆炸,所以开滚动数组,队列记录上一次的转移,head和tail也要做些小处理,答案用long long计算
然后就过了样例,欢天喜地地交上去了
不出4ms就WA了……
又试了一组数据,发现和暴力结果不同
os:好像数据中0比较多?
结果看了眼std发现要特判0?
os:???
想了好久发现判断斜率时的sum[x]−sum[y]可能会因为a[x]=0而除0出错(除以实数0会爆出一些奇怪的东西(‵~′))……
所以要判0啊!判0啊!
附上文所说的证明(转自DaD3zZ):
大体上假设某串为abcdabcd,如果最后要分割成a|b|cda|b|cd那么 先分割成ab|cdab|cd当前答案为a∗cd+b∗cda∗cd+b∗cd,再分割成a|b|cda|b|cd,答案为a∗b+a∗cd+b∗cda∗b+a∗cd+b∗cd 先分割成a|bcda|bcd当前答案为a∗bcda∗bcd,在分割成a|b|cda|b|cd,答案为a∗bcd+b∗cda∗bcd+b∗cd 那么两式化一化就可以发现得到的是相同的。所以,对于其余的也合适;
注意:判0啊!
代码:
#include<bits/stdc++.h> #define M 100002 #define LL long long using namespace std; int n,k,a[M],head[2],tail[2],q[2][M]; LL sum[M],f[2][M],ans; bool mk; double Get(bool i,int x,int y) { if (sum[x]==sum[y]) return 0; return (double)(f[i][x]-f[i][y])/(double)(sum[x]-sum[y]); } main() { scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%d",a+i), sum[i]=sum[i-1]+a[i]; head[0]=tail[0]=1; for (int i=1;i<=k;i++) { mk^=1; head[mk]=tail[mk]=1; for (int j=1;j<n;j++) { while (head[!mk]<tail[!mk]&&Get(!mk,q[!mk][head[!mk]+1],q[!mk][head[!mk]])>sum -sum[j]) head[!mk]++; f[mk][j]=f[!mk][q[!mk][head[!mk]]]+(sum -sum[j])*(sum[j]-sum[q[!mk][head[!mk]]]); while (head[mk]<tail[mk]&&Get(mk,j,q[mk][tail[mk]])>Get(mk,q[mk][tail[mk]],q[mk][tail[mk]-1])) tail[mk]--; q[mk][++tail[mk]]=j; } } for (int i=1;i<n;i++) ans=max(ans,f[mk][i]); printf("%lld",ans); }
冷静下来,重新看看题目,其实和之前的斜率DP没什么两样,但也许是觉得太模式化了,连最基本的除0错误都忽略掉了,不得不说是对于细节的把握力不够而导致的,也许再细心一些,就能在不通过外力的情况下找出这种小错误了
相关文章推荐
- 第二章:算法
- 【动态规划】凑硬币
- 高光谱IDL遥感相关网站资源
- 逻辑随笔1
- LeetCode94 Binary Tree Inorder Traversal(迭代实现) Java
- 对变量重复定义的恶习!!!!!!!!!!!!!!!!(以自编插入排序代码为例)
- SearchView搜索过滤
- [改善Java代码] 谨慎包装类型的大小比较
- 不用加减乘除做加法
- 工作总结05
- 析构函数中不能抛出异常的原因
- HDU 1875 畅通工程再续(kruskal)
- HTML中label的两种使用方法
- QT中QWidget、QDialog及QMainWindow的区别
- jquery $(":text") 这是什么意思
- 滑雪(动规例题)
- HDU 1875 畅通工程再续(kruskal)
- 求1+2+3+...+n
- poj1703 Find them, Catch them
- DMA(Linux设备驱动程序)