Online Judge | Problem Set | Authors | Online Contests | User | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest |
感觉自己斜率优化合格的人进来看看。。求交流。。下面是我的一点解题报告,但是对于边界条件——分母为0时,斜率不存在了,不存 在了再用斜率等式还有意义吗?我分情况证明了一下,发现对于这个题依然试用,但 对于其他题目呢,对于存在这种bug的问题呢??详情请看我解题报告”对于边界情 形的一点感想:“那一部分, 下面是我的解题报告: 实际上斜率优化不难,关键是理解了斜率不等式以及单调队列队尾的更新。 此题很容易得到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) --------------- 不等式(1) 化简得:[(f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1])] >= 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])] / (a[j1+1]-a[j2+1]) <= i。 对于a[j1+1]==a[j2+1]的情况,不能用除法了,只能用乘法那个表达式, 所以,如果对于决策j1,j2满足上述表达式,则j2 优于 j1。 但是如果不满足上述表达式呢,如果不满足那么j2就一定比j1差吗?? NO!!因为如果j1,j2不变,不等式(1)左边是个常数,不会变;而a[j1+1]-a[j2+1] <= 0,所以随着i的增加不等式右边是会变小的(或者不变),如果变小的话,那么 有可能在某一个i位置不等式会成立,也就是说在以后较大的某个i,j2会优于j1。 OK,然后我们看看怎样用单调队列结合这两个性质来解这个问题。 令dy(j1, j2) = (f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1]), dx(j1,j2) = a[j1+1]-a[j2+1]。 首先刚开始队首元素为0——很明显~~ 然后假设队列首尾指针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就没用了。。。。直接删了。。。 对于边界情形的一点感想:实际上上述判断斜率大小的方式在几何角度来说只能针对 x1,x2都不是0或者仅有一个是0的情况,但是对于x1,x2都是0的情况需要对y进行判 断以确定是正无穷还是负无穷(在解析几何方面说实际上是无斜率,但在这个题中却 是有意义的。),有兴趣的可以分情况去讨论一下。dy(x,y)和dy(y,z)有四种极限取 值方式,分别是:(+oo,+oo), (+oo, -oo), (-oo, +oo), (-oo, -oo),其中第1、2、4 种情况可以用代码中的乘法运算比较来判断,但是如果出现第3种情况(这时y优于 x,且y优于z),程序会把y删掉,所以这么判断是错误的。但是这个程序却可以AC 这个题,为什么??因为(-oo, +oo)的情况不会出现 - -,只会出现(-oo, -oo)的情 况;因为如果dx(x,y)==0,那么a[x+1]==a[y+1],那么-sum[x]+a[x+1]*x <= -sum[y]+a[y+1]*y,而f[x]<=f[y]恒成立,所以(f[x]-sum[x]+a[x+1]*x) - f[y]-sum[y]+a[y+1]*y)是一定小于等于 0的,也就是说如果a[x+1]==a[y+1],则 dy(x,y)一定是小于等于0的,所以对于本题来说是可以这样维护的。(说的有点乱, 有兴趣的同学可以在纸上画画,感觉对于边界的证明想的有点麻犯了;如有更好证明 方法,欢迎提出一起讨论)。 好了,主体过程就是这些,需要注意几点:因为有个限制k,所以决策点需要延迟加 入,然后就是数据——记得用long long,最好读得时候也用上,我读的时候用的 int,然后WA了,改成long long就过了,好像按说用int也行,可能是我没处理好。 #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 500010; typedef long long llg; int n, k, queue[N]; llg sum[N], f[N], a[N]; llg dy(int j1, int j2) { return (f[j1]-sum[j1]+a[j1+1]*j1) - (f[j2]-sum[j2]+a[j2+1]*j2); } llg 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; } Followed by:
Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator