poj 3709 K-Anonymous Sequence

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!

 

你可能感兴趣的:(poj 3709 K-Anonymous Sequence)