hdu3507斜率优化dp

这题: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;
}



你可能感兴趣的:(hdu3507斜率优化dp)