POJ 3709 K-Anonymous Sequence (斜率优化DP)
2015-10-11 00:15
441 查看
首先我们写出DP方程:
dp[i]=min(dp[j]+w[j][i]|0<=j<=i−k)dp[i] = min (dp[j] + w[j][i] | 0 <= j <= i-k)
其中w[j][i]=sum[i]−sum[j]−c[j+1]∗(i−j)w[j][i] = sum[i]-sum[j]-c[j+1]*(i-j)这里面的c[i]c[i]代表第ii个数。
对于k<j<i−k+1k < j< i-k+1 当更新ii时,如果j比kj比k优那么我们需要满足:
G(j,k)=(dp[j]−sum[j]+j∗c[j+1])−(dp[k]−sum[k]+k∗c[k+1])c[j+1]−c[k+1]<=iG(j,k) = \frac{(dp[j]-sum[j]+j*c[j+1])-(dp[k]-sum[k]+k*c[k+1])}{c[j+1]-c[k+1]}<=i
因为ii是单调增的,所以我们可以得到后面的jj点都比kk点优。故我们可以在单调队列中删掉k点。
我们维护一个单调队列,如果k,i−k+1k,i-k+1一定有一个都比jj点优,那么我们需要满足:
G(i−k+1,j)<=G(j,k)G(i-k+1,j)<=G(j,k)
证明很简单:
我们更新第pp个点的时候:
if(G(j,k)<=p)⟹G(i−k+1,j)<=p我们得出i−k+1点比j点优。if(G(j,k)<=p) \implies{G(i-k+1,j)<=p}我们得出i-k+1点比j点优。
if(G(j,k)>p)⟹我们得到k点比j点优if(G(j,k)>p) \implies\text{我们得到$k$点比$j$点优}
所以我们在后面都可以以删掉j点。
下面是AC代码:
dp[i]=min(dp[j]+w[j][i]|0<=j<=i−k)dp[i] = min (dp[j] + w[j][i] | 0 <= j <= i-k)
其中w[j][i]=sum[i]−sum[j]−c[j+1]∗(i−j)w[j][i] = sum[i]-sum[j]-c[j+1]*(i-j)这里面的c[i]c[i]代表第ii个数。
对于k<j<i−k+1k < j< i-k+1 当更新ii时,如果j比kj比k优那么我们需要满足:
G(j,k)=(dp[j]−sum[j]+j∗c[j+1])−(dp[k]−sum[k]+k∗c[k+1])c[j+1]−c[k+1]<=iG(j,k) = \frac{(dp[j]-sum[j]+j*c[j+1])-(dp[k]-sum[k]+k*c[k+1])}{c[j+1]-c[k+1]}<=i
因为ii是单调增的,所以我们可以得到后面的jj点都比kk点优。故我们可以在单调队列中删掉k点。
我们维护一个单调队列,如果k,i−k+1k,i-k+1一定有一个都比jj点优,那么我们需要满足:
G(i−k+1,j)<=G(j,k)G(i-k+1,j)<=G(j,k)
证明很简单:
我们更新第pp个点的时候:
if(G(j,k)<=p)⟹G(i−k+1,j)<=p我们得出i−k+1点比j点优。if(G(j,k)<=p) \implies{G(i-k+1,j)<=p}我们得出i-k+1点比j点优。
if(G(j,k)>p)⟹我们得到k点比j点优if(G(j,k)>p) \implies\text{我们得到$k$点比$j$点优}
所以我们在后面都可以以删掉j点。
下面是AC代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define LL long long #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) using namespace std; const int maxn = 500050; LL dp[maxn],sum[maxn],c[maxn]; int n,k; void init(){ scanf("%d%d",&n,&k); dp[0] = sum[0] = 0; FOR(i,1,n+1) scanf("%I64d",&c[i]); //sort(c+1,c+n+1); FOR(i,1,n+1) sum[i] = sum[i-1]+c[i]; } LL G_UP(int i,int j) {return (dp[i]-sum[i]+i*c[i+1]) - (dp[j]-sum[j]+j*c[j+1]);} LL G_DOWN(int i,int j) {return c[i+1] - c[j+1];} LL G_DP(int i,int j) {return dp[j]+sum[i]-sum[j]-c[j+1]*(i-j);} void work(){ int q[maxn],head,tail; head = tail = 0; q[tail++] = 0; FOR(i,k,n+1){ while(head+1 < tail && G_UP(q[head+1],q[head]) <= G_DOWN(q[head+1],q[head])*i) head ++; dp[i] = G_DP(i,q[head]); int s = i-k+1; if(s < k) continue; while(head+1 < tail && G_UP(s,q[tail-1]) * G_DOWN(q[tail-1],q[tail-2]) <= G_DOWN(s,q[tail-1])*G_UP(q[tail-1],q[tail-2])) tail --; q[tail++] = i-k+1; } printf("%I64d\n",dp ); } int main() { //freopen("test.in","r",stdin); int T; scanf("%d",&T); while(T--){ init(); work(); } return 0; }
相关文章推荐
- [RMQ]UVa 11235 - Frequent values
- N-Queens II
- 快速排序(QuickSort)
- xcode4中build Settings常见参数解析
- UVA 11235 Frequent values(RMQ)
- IOS笔记UI--scrollview
- EasyUI - Progressbar 进度条控件
- MAC 下如何合并.cue 和 .bin 文件成为 .ISO
- EasyUI - LinkButton 按钮控件
- 改变UISearchBar的背景色
- easyui 动态修改窗口title
- NSLocalizedDescription=Request failed: unacceptable content-type: text/html 解决方法
- poj 2785 4 Values whose Sum is 0
- Request简单总结
- 用GUI实现求两个数的加法--方法二
- 用GUI实现求两个数的加法--方法一
- UICollectionView
- ACM学习历程—HDU4675 GCD of Sequence(莫比乌斯)
- build.gradle 文件
- 在UICollectionView上面添加分割线