这题:n可能取500000,o(n^2)就会超时吧,所有只能优化。
注意到这题的动态规划方程:dp[i]=max(dp[j]+(sum[i]-sum[j])^2+m;
化简下得 :dp[i]=max(dp[j]+sum[i]^2+sum[j]^2-2*sum[i]*sum[j]); 无法直接用单调队列优化,i和j不能分开。
假设j比k优
dp[j]+sum[i]^2+sum[j]^2-2*sum[i]*sum[j]<dp[k]+sum[i]^2+sum[k]^2-2*sum[i]*sum[k];
即:
dp[j]+sum[j]^2-(dp[k]+sum[k]^2)/(2*dp[j]-2*dp[k])<sum[i]
即可以看做是两个点的斜率小于sum[i],这里用g[j,k]表示j,k两点的斜率。
这里用到两个结论:
1.g[j,k]<sum[i];则j比k更优。
2.g[i,j]<g[j,k]说明j不可能最优。
说明一下:
g[i,j]<sum[i],则i更优。
g[i,j]>sum[i],则g[j,k]>g[i,j]>sum[i];k更优
特别注意:
1.这里sum[i]是递增的,g[j,k](斜率)也是递增的,所以每次不满足斜率大于sum[i]的点,以后也不会大于某个sum[i],所以要出队。计算dp[i]时从队首找最优的,即不满足g[q[head+1],q[head]]<=sum[i]的点,即找到斜率大于sum[i]的点,并将小于或等于sum[i]的点出队。等于的情况也是不可以的。
2.每次入队时要保证斜率的单调递增性。i入队,g[i,j]>g[j,k];
下面是代码:
#include<iostream> #include<vector> #include<string> #include<queue> #include<map> #include<cstdio> #include<cstring> #define maxn 500005 #define INF 0xfffffff #define min(a,b) a<b?a:b #define max(a,b) a>b?a:b using namespace std; int n,m,head,tail; int sum[maxn],q[maxn],dp[maxn]; int getUp(int i,int j) { return dp[i]+sum[i]*sum[i]-dp[j]-sum[j]*sum[j]; } int getDown(int i,int j) { return 2*sum[i]-2*sum[j]; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { sum[0]=dp[0]=0;//当不打印的时候费用为0 for(int i=1;i<=n;i++) { scanf("%d",&sum[i]); sum[i]=sum[i-1]+sum[i]; } head=tail=0; q[tail++]=0;//将0入队,因为求斜率时至少有2个点 for(int i=1;i<=n;i++) { while(head+1<tail&&getUp(q[head+1],q[head])<=sum[i]*getDown(q[head+1],q[head]))//第二个比第一个优g[j,k]<=sum[i] head++; dp[i]=dp[q[head]]+m+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]]); 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]))//最后一个比前一个差g[i,j]<=g[j,k]满足斜率的当掉递增 tail--; q[tail++]=i; } printf("%d\n",dp[n]); } return 0; }