poj 3709 K-Anonymous Sequence

来源:互联网 发布:淘宝网名片 编辑:程序博客网 时间:2024/05/22 07:54

http://poj.org/problem?id=3709

/*
O(n^2)的DP方程:f[i]=Min{f[j]+sum[i]-sum[j]-a[j+1]*(i-j)}。

假设决策j1<j2并且j2优于(或者不差于)j1,那么

f[j1]+sum[i]-sum[j1]+a[j1+1]*(i-j1) >= f[j2]+sum[i]-sum[j2]-a[j2+1]*(i-j2)

[(f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1]*j2)] >= i*(a[j1+1]-a[j2+1])。

a[j2+1]>=a[j1+1], 所以a[j1+1]-a[j2+1] <= 0。

可以写成:[(f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1]*j2)] / (a[j1+1]-a[j2+1]) <= i。

对于a[j1+1]==a[j2+1]的情况,不能用除法了,只能用乘法那个表达式,

所以,如果对于决策j1,j2满足上述表达式,则j2 优于 j1。

首先刚开始队首元素为0

用dy(i,j)表示[(f[i]-sum[i]+a[i+1]*i) - (f[j]-sum[j]+a[j+1]*j)]
用dx(i,j)表示(a[i+1]-a[j+1])*i
然后假设队列首尾指针head < tail 并且dy(queue[head],queue[head+1]) >=
i*dx(queue[head],queue[head+1]),那么队首元素直接丢掉就可以了。因为i是递
增的,如果当前queue[head]没有queue[head+1]好,那么今后也不会。
对于队尾的2个原素x, y来说,
如果对于当前i,y比x要差,那么由前面的证明:对于比较大的i,y不一定就比x差,
有可能比x好呢,我们来看看队尾3个元素的情况:x,y,z,如果
dy(x,y)/dx(x,y)>=dy(y,z)/dx(y,z),那么可以直接把y给删了。因为
dy(x,y)/dx(x,y)和dy(y,z)/dx(y,z)是个常数,对于某个i,如果dy(x,y)/dx(x,y)<=i的
话,那么dy(y,z)/dx(y,z)一定也小于等于i,也就是说:如果y优于x,那么z一定优于
y,这个时候留着y就没用了。。。。直接删了。。。

过程就是这些,另外:因为有个限制k,所以决策点需要延迟加
入。
*/

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXX = 500010;int n, k, queue[MAXX];__int64 sum[MAXX], f[MAXX], a[MAXX];__int64 dy(int j1, int j2){    return  (f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1]*j2);}__int64 dx(int j1, int j2){    return  (a[j1+1] - a[j2+1]);}void dp(){    int i, j, head, tail, x, y, z;    head = tail = 0;    queue[0] = 0;    for(i = 1; i <= n; i++)    {        while(head<tail && dy(queue[head], queue[head+1])>=i*dx(queue[head], queue[head+1]))            head++;        j = queue[head];        f[i] = f[j] + sum[i] - sum[j] - a[j+1]*(i-j);        if(i >= 2*k-1)    //实际上是i-k+1>=k        {            z = i-k+1;            while(head < tail)            {                x = queue[tail-1];                y = queue[tail];                if(dy(x,y)*dx(y,z) >= dy(y,z)*dx(x,y))  tail--;                else  break;            }            queue[++tail] = z;        }    }}int main(){    int t, i;    scanf("%d", &t);    while(t--)    {        scanf("%d%d", &n, &k);        sum[0] = 0;        for(i = 1; i <= n; i++)        {            scanf("%I64d", a+i);            sum[i] = sum[i-1] + a[i];        }        dp();        printf("%I64d\n", f[n]);    }    return 0;}


ok!

 

原创粉丝点击