| ||||||||||
| 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