HDU 3507 Print Article DP(斜率优化入门)

来源:互联网 发布:淘宝恶意差评怎么办 编辑:程序博客网 时间:2024/06/14 03:42

点击打开链接

参考链接

题意:n个数有代价ci n<=5e5,若将k个数连续写在一起的代价为:(sigma(c[i]))^2+M i=x+1~x+k,M为常数 求写出n个数的最小代价
s[i]为i的前缀和,设d[i] 写出前i个数的最小代价 d[i]=min(d[j]+(sum[i]-sum[j])^2+M) j=1~i j+1~i为最后一段长度
n<=5e5,直接做O(n^2)显然TLE,注意方程中sum[i]是递增的,利用斜率优化 时间复杂度降为O(n)


结论1:

考虑决策j,k 若决策j优于决策k d[j]+(sum[i]-sum[j])^2+M<d[k]+(sum[i]-sum[k])^2+M
化解得 (d[j]+sum[j]^2)-(d[k]+sum[k]^2)/(2*(sum[j]-sum[k])) < sum[i] 令yi=d[i]-sum[i]^2,xi=2*sum[i]
则:(yj-yk)/(xj-xk)<sum[i] 得决策j优于k当且仅当斜率g(j,k)<sum[i]


结论2:

k<j<i 若g(i,j)<g(j,k) 则j永远都不可能为最优决策 可以永久剔除 
情况1:g(i,j)<sum[x] i优于j
情况2:g(i,j)>sum[x] j优于i 但是g(j,k)>g(i,j)>sum[x] k优于j.


则只剩下点满足g(i,j)>g(j,k) 从左到右斜率是递增的
可以用单调队列来维护可能的最优决策,队列元素a,b,c 
新的点为d,若g(d,c)<g(c,b) 由上可知,队尾c出队 直到g(d,c')>=g(c',b') d加入末尾 ,队列中相邻两点的斜率是递增的. 


对于某个状态i,若g(head+1,head)<sum[i] 则head+1优于head 又因为sum[i]是递增的 所以head可以出队(后面肯定不会用到)
直到g(head+1,head)>=sum[i] head优于head+1,由于单调队列递增 后面g(head+2,head+1)>=sum head+k优于head+k+1 k=0,1,2...
此时head为最优决策 执行DP(i,q[head])即可

  

#include <bits/stdc++.h>using namespace std;const int N=5e5+20; int n,M,c[N],s[N]; int dp[N];int head,tail,q[N];int DP(int i,int j){return dp[j]+(s[i]-s[j])*(s[i]-s[j])+M;}int dy(int j,int k)//yj-yk{return (dp[j]+s[j]*s[j])-(dp[k]+s[k]*s[k]);}int dx(int j,int k)//xj-xk{return 2*(s[j]-s[k]);}int main(){while(cin>>n>>M){s[0]=dp[0]=0;for(int i=1;i<=n;i++)scanf("%d",&c[i]),s[i]=s[i-1]+c[i];head=tail=0;q[tail++]=0;//决策:取一整段//每个点最多进出队一次O(N) for(int i=1;i<=n;i++){//找到最优决策head,根据sum[i]递增&&g(h+1,h)<=sum[i] 淘汰headwhile(head+1<tail&&dy(q[head+1],q[head])<=s[i]*dx(q[head+1],q[head]))head++;dp[i]=DP(i,q[head]);//g(i,j)<g(j,k) 维护最优决策集合 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;}cout<<dp[n]<<endl; }return 0;}






0 0
原创粉丝点击