您的位置:首页 > 产品设计 > UI/UE

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代码:

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: