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!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值