斜率优化DP和四边形不等式优化DP整理
2016-08-22 21:35
218 查看
当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环
(一重循环跑状态 i,一重循环跑 i 的所有子状态)这样的时间复杂度是O(N^2)而 斜率优化或者四边形不等式优化后的DP
可以将时间复杂度缩减到O(N)
O(N^2)可以优化到O(N) ,O(N^3)可以优化到O(N^2),依次类推
斜率优化DP和四边形不等式优化DP主要的原理就是利用斜率或者四边形不等式等数学方法
在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举i的子状态的循环,几乎就是直接把最优的子状态找出来了
其中四边形不等式优化是用数组s边跑边求最优的子状态,例如用s[i][j]保存dp[i][j]的最优子状态
斜率优化的话是将后面可能用到的子状态放到队列中,要求的当前状态的最优状态就是队首元素q[head]
另外,网上见到很多用二分+DP解斜率优化的问题。
以dp求最小值为例:
主要的解题步骤就是先写出dp的状态转移方程,然后选取两个子状态p,q
假设p < q而决策q比p更好,求出斜率不等式,然后就可以写了
至于经常有题目控制子决策的范围什么的(比如控制区间长度,或者控制分组的组数),就需要具体情况具体分析
这题绝壁是最最最适合入门的斜率DP的题,我发誓!!!
版本一:(O(N^2))
对于状态转移方程dp[i] = dp[j] + (sum[i]-sum[j]+10)*w[i]
考虑 k < j < i 且假设 i状态由j状态转移得到比由k状态转移得到更优
即:dp[j] + (sum[i]-sum[j]+10)*w[i] <= dp[k] + (sum[i] - sum[k] + 10)*w[i]
(这里取小于号是因为dp保存的是最小花费,花费越小越好,取等是因为j比k大,所以就算k,j一样优也选j)
这个不等式化简之后就是
dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])
这里的w[i]满足单调递增
有了上面的不等式和单调条件就可以斜率优化了,主要做法就是利用单调队列维护满足的点
比如j状态优于k状态,就可以将k永远的剔除了
具体对于子状态的维护见代码里面有2个对队列进行的删除的操作,一个是在求dp[i]时在队首删除
一个是在将状态i加入队列时在队尾删除的操作
版本二:(O(N))
列状态转移方程 然后假设 k < j < i 且j决策更好 不等式列出来就好了
如果现在有人能过的话请联系博主 ,不胜感基、、、
状态转移方程写的好的话不用优化也可以过
这个题的状态转移的方程很经典啊
二分做的。。。。
和第9题一样的想法 但是存不下 换了个方法
(一重循环跑状态 i,一重循环跑 i 的所有子状态)这样的时间复杂度是O(N^2)而 斜率优化或者四边形不等式优化后的DP
可以将时间复杂度缩减到O(N)
O(N^2)可以优化到O(N) ,O(N^3)可以优化到O(N^2),依次类推
斜率优化DP和四边形不等式优化DP主要的原理就是利用斜率或者四边形不等式等数学方法
在所有要判断的子状态中迅速做出判断,所以这里的优化其实是省去了枚举i的子状态的循环,几乎就是直接把最优的子状态找出来了
其中四边形不等式优化是用数组s边跑边求最优的子状态,例如用s[i][j]保存dp[i][j]的最优子状态
斜率优化的话是将后面可能用到的子状态放到队列中,要求的当前状态的最优状态就是队首元素q[head]
另外,网上见到很多用二分+DP解斜率优化的问题。
以dp求最小值为例:
主要的解题步骤就是先写出dp的状态转移方程,然后选取两个子状态p,q
假设p < q而决策q比p更好,求出斜率不等式,然后就可以写了
至于经常有题目控制子决策的范围什么的(比如控制区间长度,或者控制分组的组数),就需要具体情况具体分析
1 HDU 1300 Pearls
最最最简单的斜率DP优化的题,就算不用优化,O(N^2)的算法也可以AC这题绝壁是最最最适合入门的斜率DP的题,我发誓!!!
版本一:(O(N^2))
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* dp[i]表示买前i种珍珠的最少花费 dp[i] = min(dp[j] + (sum[i] - sum[j] + 10)*w[i]) 其中sum[i]-sum[j]表示第j+1种珍珠到第i种珍珠所需的数量 w[i]表示第i种珍珠的价值 */ const int N = 111; int w ,dp ,sum ; int main() { int T;Sint(T); while (T--) { int n;Sint(n); for (int i = 1,x;i <= n;++i) { Sint2(x,w[i]); sum[i] = sum[i-1] + x; } dp[1] = (sum[1]+10)*(w[1]); for (int i = 2;i <= n;++i) { dp[i] = dp[i-1] + (sum[i]-sum[i-1]+10)*w[i]; for (int j = 0;j < i-1;++j) { dp[i] = min(dp[i],dp[j] + (sum[i]-sum[j]+10)*w[i]); } } Pintc(dp ,'\n'); } return 0; }当做出暴力DP版本之后,只需再多考虑一步就可以变成斜率优化DP
对于状态转移方程dp[i] = dp[j] + (sum[i]-sum[j]+10)*w[i]
考虑 k < j < i 且假设 i状态由j状态转移得到比由k状态转移得到更优
即:dp[j] + (sum[i]-sum[j]+10)*w[i] <= dp[k] + (sum[i] - sum[k] + 10)*w[i]
(这里取小于号是因为dp保存的是最小花费,花费越小越好,取等是因为j比k大,所以就算k,j一样优也选j)
这个不等式化简之后就是
dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])
这里的w[i]满足单调递增
有了上面的不等式和单调条件就可以斜率优化了,主要做法就是利用单调队列维护满足的点
比如j状态优于k状态,就可以将k永远的剔除了
具体对于子状态的维护见代码里面有2个对队列进行的删除的操作,一个是在求dp[i]时在队首删除
一个是在将状态i加入队列时在队尾删除的操作
版本二:(O(N))
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* dp[i]表示买前i种珍珠的最少花费 dp[i] = min(dp[j] + (sum[i] - sum[j] + 10)*w[i]) 其中sum[i]-sum[j]表示第j+1种珍珠到第i种珍珠所需的数量 w[i]表示第i种珍珠的价值 dp[j] - dp[k] <= w[i]*(sum[j]-sum[k]) The qualities of the classes (and so the prices) are given in ascending order. So w[i]单增 --斜率DP */ const int N = 111; int w ,dp ,sum ; int q ; int DP(int i,int j) { return dp[j] + (sum[i]-sum[j]+10)*w[i]; } int dy(int i,int j) { return dp[i]-dp[j]; } int dx(int i,int j) { return sum[i]-sum[j]; } int main() { int T;Sint(T); while (T--) { int n;Sint(n); for (int i = 1,x;i <= n;++i) { Sint2(x,w[i]); sum[i] = sum[i-1] + x; } int head = 0,tail = 0; q[tail++] = 0; for (int i = 1;i <= n;++i) { while (head+1<tail&&dy(q[head+1],q[head])<=w[i]*dx(q[head+1],q[head])) ++head; dp[i] = DP(i,q[head]); while (head+1<tail&&dy(i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail; q[tail++] = i; } Pintc(dp ,'\n'); } return 0; }
2 POJ 1260 Pearls 和上面一题一样的
3.HDU 3507 Print Article
列状态转移方程 然后假设 k < j < i 且j决策更好 不等式列出来就好了#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int N = 500007; int dp ; int sum ; int q ; int n,m; int EX(int x) { return x*x; } int getDP(int i,int j) { return dp[j] + m + EX(sum[i]-sum[j]); } int getUP(int j,int k)//yj - yk { return (dp[j] + EX(sum[j])) - (dp[k] + EX(sum[k])); } int getDown(int j,int k)//xj - xk { return 2*(sum[j] - sum[k]); } int main() { while (Sint2(n,m) == 2) { for (int i = 1,x;i <= n;++i) { Sint(x); sum[i] = sum[i-1] + x; } int head = 0,tail = 0; q[tail++] = 0;//单调队列 (单增) for (int i = 1;i <= n;++i) { // getup/getdown <= sum[i] while (head+1<tail&&getUP(q[head+1],q[head])<=sum[i]*getDown(q[head+1],q[head])) head++; dp[i] = getDP(i,q[head]); // getup(i,q[tail-1])/getdown(i,q[tail-1]) <= getup(q[tail-1],q[tail-2])/getdown(q[tail-1],q[tail-2]) while (head+1<tail&&getUP(i,q[tail-1])*getDown(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDown(i,q[tail-1]))tail--; q[tail++] = i; } Pintc(dp ,'\n'); } return 0; }
4.HDU 2829 Lawrence
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Schars(s) scanf("%s",s) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* dp[i][j]表示前j个数分成i组的最小价值 sum[i]表示前i个数的和 cost[i]表示前i个数的花费 */ const int N = 1004; int sum ,cost ,dp ; int q ,head,tail; int n,m; int EX(int x) { return x*x; } int dy(int x,int j,int i) { return dp[x][i] - cost[i] + EX(sum[i]) - (dp[x][j] - cost[j] + EX(sum[j])); } int dx(int j,int i) { return sum[i] - sum[j]; } int DP(int x,int j,int i) { return dp[x][i]+cost[j] - cost[i] - sum[i]*(sum[j]-sum[i]); } int main() { while (Sint2(n,m)==2&&(n||m)) { ++m; for (int i = 1,x;i <= n;++i) { Sint(x); sum[i] = sum[i-1] + x; cost[i] = cost[i-1] + sum[i-1]*x; } for (int i = 1;i <= n;++i) dp[1][i] = cost[i]; for(int i = 2;i <= m;++i) { head = tail = 0; q[tail++] = i-1; for (int j = i;j <= n;++j) { while (head+1<tail&&dy(i-1,q[head],q[head+1])<=sum[j]*dx(q[head],q[head+1])) head++; dp[i][j] = DP(i-1,j,q[head]); while (head+1<tail&&dx(q[tail-2],q[tail-1])*dy(i-1,q[tail-1],j)<=dy(i-1,q[tail-2],q[tail-1])*dx(q[tail-1],j)) --tail; q[tail++] = j; } } Pintc(dp[m] ,'\n'); } return 0; }
5.HDU 2993 MAX Average Problem
这题、、、如果现在有人能过的话请联系博主 ,不胜感基、、、
6.UVALive 5097 - Cross the Wall
贪心处理下#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%lld",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%lld %lld",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%lld%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* dp[i][j]表示 前i个人挖j个洞的最小花费 1.当w[i] <= w[j] && h[i]<=h[j]时 舍弃 (w[i],h[i]) 2.将人按w递增 h递减 排序,即满足 w[j] < w[i]&&h[j] > h[i] 故dp[i][j] = dp[k][j-1] + w[i]*h[k+1] */ const int N = 500007; struct Node { ll h,w; }b ; int q ,head,tail; bool cmp(Node a,Node b) { if (a.h == b.h) return a.w > b.w; return a.h > b.h;//确保h递减 } ll dp [104]; ll dy(int j,int k,int t) { return dp[j][t] - dp[k][t]; } ll dx(int j,int k) { return b[k+1].h - b[j+1].h; } int main() { int n,k; while (Sint2(n,k) == 2) { for (int i = 1;i <= n;++i) { Sll2(b[i].w,b[i].h); } sort(b+1,b+n+1,cmp); int t = 1; for (int i = 1;i <= n;++i) { if (b[t].w < b[i].w) b[++t] = b[i]; } // cout<<t<<endl; k = min(t,k); for (int i = 1;i <= t;++i) dp[i][1] = b[i].w*b[1].h; for (int j = 2;j <= k;++j) { head = tail = 0;mem(q,0); q[tail++] = 0; for (int i = 1;i <= t;++i) { while (head+1<tail&&dy(q[head+1],q[head],j-1) <= b[i].w * dx(q[head+1],q[head])) ++head; dp[i][j] = dp[q[head]][j-1] + b[i].w * b[q[head]+1].h; while (head+1<tail&&dy(i,q[tail-1],j-1)*dx(q[tail-1],q[tail-2]) <= dy(q[tail-1],q[tail-2],j-1)*dx(i,q[tail-1])) --tail; q[tail++] = i; } } ll ans = dp[t][1]; for (int i = 2;i <= k;++i) { ans = min(ans,dp[t][i]); } Pllc(ans,'\n'); } return 0; }
7.HDU 3045 Picnic Cows
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* Cows in the same team should reduce their Moo~ to the one who has the lowest Moo~ in this team dp[i]表示前i头牛的最小花费 dp[i] = dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1]) dp[j]-dp[k]+sum[k]-sum[j]+j*a[j+1]-k*a[k+1] < i*(a[j+1]-a[k+1]) */ const int N = 400004; ll dp ,a ,sum ; int q ,head,tail; ll dy(int j,int k) { return dp[j]-dp[k] + sum[k]-sum[j] + j*a[j+1]-k*a[k+1]; } ll dx(int j,int k) { return a[j+1] - a[k+1]; } ll DP(int i,int j) { return dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1]); } int main() { int n,t; while (Sint2(n,t) == 2) { for (int i = 1;i <= n;++i) Sll(a[i]); sort(a+1,a+n+1); for (int i = 1;i <= n;++i) sum[i] = sum[i-1] + a[i]; head = tail = 0; q[tail++] = 0; for (int i = 1;i <= n;++i) { while (head+1<tail&&dy(q[head+1],q[head]) <= i*dx(q[head+1],q[head])) ++head; dp[i] = DP(i,q[head]); int j = i-t+1; if (j < t) continue; while (head+1<tail&&dy(j,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(j,q[tail-1])) --tail; q[tail++] = j; } Pllc(dp ,'\n'); } return 0; }
8.HDU 3516 Tree Construction
斜率DP写的怎么都过不了,最后用四边形不等式,要保证j-i递增于是枚举长度#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; /* dp[i][j]表示从i到j所需的最小花费 dp[i][j] = min {dp[i][k] + dp[k+1][j] + w(i,k,j)} w(i,k,j) = y[k] - y[j] + x[k+1] - x[i] s[i][j] = k 表示 dp[i][j]这个状态最优的决策是 k */ const int N = 1003; const int inf = 0x3f3f3f3f; int dp ,s ; int x ,y ; int w(int i,int k,int j) { if (k >= j) return inf; return y[k] - y[j] + x[k+1] - x[i]; } int DP(const int &n) { mem(dp,0);int tmp; for (int L = 2;L<=n;++L) //以j-i递增为顺序递推 { for (int i = 1,j = L;i+L-1<=n;++i,j = i+L-1)//i 是 区间左端点,j是区间右端点 { dp[i][j] = inf; for (int k = s[i][j-1];k <= s[i+1][j];++k) { tmp = dp[i][k] + dp[k+1][j] + w(i,k,j); if (tmp < dp[i][j]) { dp[i][j] = tmp; s[i][j] = k; } } } } return dp[1] ; } int main() { int n; while (Sint(n) == 1) { for (int i = 1;i <= n;++i) { Sint2(x[i],y[i]); s[i][i] = i; } Pintc(DP(n),'\n'); } return 0; }
9.POJ 1160 Post Office
状态转移方程写的好的话不用优化也可以过 这个题的状态转移的方程很经典啊
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* dp[i][j]表示在前i个村庄建j个邮局的最小值 d[i][j]表示在[i,j]区间建1个邮局的最小值 dp[i][j] = min{dp[k][j-1] + d[k+1][j]} (1<=k<j) */ const int N = 304; int V,P; int x ; int dp [33]; int d ; int DP() { for (int i = 1;i <= V;++i) { for (int j = i+1;j <= V;++j) { d[i][j] = d[i][j-1] + x[j] - x[(i+j)/2]; } } for (int i = 1;i <= V;++i) dp[i][0] = inf; for (int i = 1;i <= V;++i) { for (int j = 1;j <= min(i,P);++j) { // if (j > P) break; dp[i][j] = inf; for (int k = j-1;k < i;++k) { dp[i][j] = min(dp[i][j],dp[k][j-1]+d[k+1][i]); } } } return dp[V][P]; } int main() { while (Sint2(V,P) == 2) { for (int i = 1;i <= V;++i) Sint(x[i]); sort(x+1,x+V+1); Pintc(DP(),'\n'); } return 0; }
10.POJ 1180 Batch Scheduling
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* dp[i]表示前i个job的最小花费 dp[i] = dp[j] + (S+sumt[i] - sumt[j]) *(sumf[i]-sumf[j]) */ const int N = 10004; ll dp ; ll sumt ,sumf ; ll t ,f ; int S,n; int q ,head,tail; ll dy(int j,int k) { return dp[j] - dp[k]; } ll dx(int j,int k) { return sumt[j] - sumt[k]; } ll DP(int i,int j) { return dp[j] + (S + sumt[i]-sumt[j])*sumf[i]; } int main() { while (Sint2(n,S) == 2) { for (int i = n;i >= 1;--i) Sll2(t[i],f[i]); for (int i = 1;i <= n;++i) { sumt[i] = sumt[i-1] + t[i]; sumf[i] = sumf[i-1] + f[i]; } head = tail = 0; q[tail++] = 0; for (int i = 1;i <= n;++i) { while (head+1<tail&&dy(q[head],q[head+1])>dx(q[head],q[head+1])*sumf[i]) ++head; dp[i] = DP(i,q[head]); while (head+1<tail&&dy(q[tail-1],i)*dx(q[tail-2],q[tail-1])<dy(q[tail-2],q[tail-1])*dx(q[tail-1],i)) --tail; q[tail++] = i; } Pllc(dp ,'\n'); } return 0; }
11.POJ 2018 Best Cow Fences
二分做的。。。。#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sdb(n) scanf("%lf",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* 虽然据说 斜率DP 更牛逼 虽然据说 二分很 low 。。。 。。。 */ const int N = 100004; const double esp = 1e-7; double a ,sum ; int n,f;//n块地至少分f分 bool ok(double mid) { double div = sum[f-1] - (f-1)*mid; for (int i = f;i <= n;++i) { div += a[i] - mid; div = max(div,sum[i]-sum[i-f]-f*mid); if (div > -esp) return 1; } return 0; } int main() { while (Sint2(n,f) == 2) { double l = inf,r = 0; for (int i = 1;i <= n;++i) { Sdb(a[i]); sum[i] = sum[i-1] + a[i]; l = min(l,a[i]); r = max(r,a[i]); } while (r-l>=esp) { double mid = (l+r)/2.0; if (ok(mid)) l = mid; else r = mid; } Pintc((int)(r*1000),'\n'); } return 0; }
12.POJ 3709 K-Anonymous Sequence
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* dp[i]表示前i个数的最小值 dp[i] = dp[j] + (sum[i]-sum[j]) - (i-j)*a[j+1] dp[j]-dp[k] - (sum[j]-sum[k]) + j*a[j+1] - k*a[k+1] < i*(a[j+1]-a[k+1]) */ const int N = 500004; ll dp ; ll sum ; ll a ; int q ,head,tail; int n,k; ll dy(int j,int k) { return dp[j]-dp[k] - (sum[j]-sum[k]) + j*a[j+1] - k*a[k+1]; } ll dx(int j,int k) { return a[j+1] - a[k+1]; } ll DP(int i,int j) { return dp[j] + (sum[i]-sum[j]) - (i-j)*a[j+1]; } int main() { int T;Sint(T); while (T--) { Sint2(n,k); for (int i = 1;i <= n;++i) { Sll(a[i]); sum[i] = sum[i-1] + a[i]; } head = tail = 0; q[tail++] = 0; for (int i = k;i <= n;++i) { while (head+1<tail&&dy(q[head+1],q[head])<=i*dx(q[head+1],q[head])) ++head; dp[i] = DP(i,q[head]); // cout<<"head = "<<q[head]<<" : "; // printf("dp[%d] = %d\n",i,dp[i]); int j = i - k + 1; if (j < k) continue; while (head+1<tail&&dy(j,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(j,q[tail-1])) --tail; q[tail++] = j; } Pllc(dp ,'\n'); } return 0; }
13.UVA 12594 Naming Babies
(找不到链接额。。。)
和第9题一样的想法 但是存不下 换了个方法#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* dp[i][j]表示前i个字符分成j份的最小值 d[i][j]表示 [i,j] 当成一份的花费 dp[i][j] = dp[k][j-1] + d[k+1][j] but d[20000][20000]开不下。。。 so 换个方法算d[i][j] sum1[i]表示前i个数的和 sum2[i]表示 求和((i-1)*a[i]) sum3[i]表示 求和(a[i]*a[i]) 故 dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k]) */ const int N = 20004; ll dp [504],sum1 ,sum2 ,sum3 ; int a ; int mp[30]; char NP[30]; char NM ; int q ,head,tail; int K; int n; void init() { Schars(NP);Sint(K); Schars(NM); for (int i = 0;i < strlen(NP);++i) { mp[NP[i] - 'a'] = i; } n = strlen(NM); for (int i = 0;i < n;++i) { a[i+1] = mp[NM[i] - 'a']; } for (int i = 1;i <= n;++i) { sum1[i] = sum1[i-1] + a[i]; sum2[i] = sum2[i-1] + (i-1)*a[i]; sum3[i] = sum3[i-1] + a[i]*a[i]; } } //dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k]) ll DP(int i,int j,int k) { return dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i] - sum1[k]) - (sum3[i] - sum3[k]); } ll dy(int j,int q,int p) { return dp[q][j-1]-dp[p][j-1] + q*sum1[q] - p*sum1[p] - (sum2[q]-sum2[p]) + (sum3[q]-sum3[p]); } ll dx(int q,int p) { return q-p; } ll solve() { for (int i = 1;i <= n;++i) dp[i][1] = sum2[i] - sum3[i]; for (int j = 2;j <= K;++j) { head = tail = 0; q[tail++] = 0; for (int i = 1;i <= n;++i) { while (head+1<tail&&dy(j,q[head+1],q[head])<=sum1[i]*dx(q[head+1],q[head])) ++head; dp[i][j] = DP(i,j,q[head]); while (head+1<tail&&dy(j,i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(j,q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail; q[tail++] = i; } } return dp [K]; } int kas; int main() { int T;Sint(T); while (T--) { init(); printf("Case %d: %lld\n",++kas,solve()); } return 0; }
14.HDU 3480 Division
#define mem(a,x) memset(a,x,sizeof(a)) #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<stack> #include<cmath> #include<map> #include<stdlib.h> #include<cctype> #include<string> #define Sint(n) scanf("%d",&n) #define Sll(n) scanf("%I64d",&n) #define Schar(n) scanf("%c",&n) #define Schars(s) scanf("%s",s) #define Sint2(x,y) scanf("%d %d",&x,&y) #define Sll2(x,y) scanf("%I64d %I64d",&x,&y) #define Pint(x) printf("%d",x) #define Pllc(x,c) printf("%I64d%c",x,c) #define Pintc(x,c) printf("%d%c",x,c) using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; /* dp[i][j]表示前i个字符分成j份的最小值 d[i][j]表示 [i,j] 当成一份的花费 dp[i][j] = dp[k][j-1] + d[k+1][j] but d[20000][20000]开不下。。。 so 换个方法算d[i][j] sum1[i]表示前i个数的和 sum2[i]表示 求和((i-1)*a[i]) sum3[i]表示 求和(a[i]*a[i]) 故 dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k]) */ const int N = 20004; ll dp [504],sum1 ,sum2 ,sum3 ; int a ; int mp[30]; char NP[30]; char NM ; int q ,head,tail; int K; int n; void init() { Schars(NP);Sint(K); Schars(NM); for (int i = 0;i < strlen(NP);++i) { mp[NP[i] - 'a'] = i; } n = strlen(NM); for (int i = 0;i < n;++i) { a[i+1] = mp[NM[i] - 'a']; } for (int i = 1;i <= n;++i) { sum1[i] = sum1[i-1] + a[i]; sum2[i] = sum2[i-1] + (i-1)*a[i]; sum3[i] = sum3[i-1] + a[i]*a[i]; } } //dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k]) ll DP(int i,int j,int k) { return dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i] - sum1[k]) - (sum3[i] - sum3[k]); } ll dy(int j,int q,int p) { return dp[q][j-1]-dp[p][j-1] + q*sum1[q] - p*sum1[p] - (sum2[q]-sum2[p]) + (sum3[q]-sum3[p]); } ll dx(int q,int p) { return q-p; } ll solve() { for (int i = 1;i <= n;++i) dp[i][1] = sum2[i] - sum3[i]; for (int j = 2;j <= K;++j) { head = tail = 0; q[tail++] = 0; for (int i = 1;i <= n;++i) { while (head+1<tail&&dy(j,q[head+1],q[head])<=sum1[i]*dx(q[head+1],q[head])) ++head; dp[i][j] = DP(i,j,q[head]); while (head+1<tail&&dy(j,i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(j,q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail; q[tail++] = i; } } return dp [K]; } int kas; int main() { int T;Sint(T); while (T--) { init(); printf("Case %d: %lld\n",++kas,solve()); } return 0; }
相关文章推荐
- 【转】斜率优化DP和四边形不等式优化DP整理
- [省选前题目整理][BZOJ 1911][APIO 2010]特别行动队(斜率优化DP)
- [省选前题目整理][BZOJ 1010][HNOI 2008]玩具装箱toy(斜率优化DP)
- 【整理】斜率or单调队列优化dp
- [省选前题目整理][BZOJ 1096][ZJOI2007]仓库建设(斜率优化DP)
- HDU 2829 Lawrence (斜率优化DP或四边形不等式优化DP)
- [省选前题目整理][BZOJ 3675][APIO 2014]序列分割(斜率优化DP)
- HDU 2993 MAX Average Problem(斜率优化DP)
- dp专辑 C - K-Anonymous Sequence [ 斜率优化]
- dp专辑 T - MAX Average Problem [ 斜率优化]
- HDU 3507 Print Article(斜率DP优化)
- 【斜率优化dp】HDU 2993
- 【斜率优化DP】玩具装箱
- 单调队列+斜率优化的DP
- HDU 3507 Print Article(DP+斜率优化)
- dp优化--斜率
- 【斜率优化DP】Batch_Scheduling
- dp专辑 B - Batch Scheduling [ 斜率优化]
- 【斜率优化DP】特别行动队
- Poj1180 Batch Scheduling --- DP的斜率优化