【模拟试题】甲虫
Description
一只甲虫发现它呆在一根狭窄的水平树枝上。“我呆在一根狭窄的水平树枝上,”甲虫心想,“我感觉象呆在X轴上一样”(这是一只数学思维相当深刻的甲虫!)在那根树枝上还有N滴露珠,每一滴含有M单位的水分,它们的位置分别在整点坐标x1, x2, . . . , xn。甲虫的初始位置在坐标原点。
天气很热,在一个单位时间里每滴露珠都会蒸发掉1单位的水分。甲虫很口渴,它一走到一滴露珠前就会在0单位时间内把它喝光。
甲虫一个单位时间能爬行一个单位的长度。令甲虫头晕的问题是:所有的爬行都值得吗?
请你写一个程序,对于给定的露珠坐标,计算出甲虫能喝到的最多的水量。
天气很热,在一个单位时间里每滴露珠都会蒸发掉1单位的水分。甲虫很口渴,它一走到一滴露珠前就会在0单位时间内把它喝光。
甲虫一个单位时间能爬行一个单位的长度。令甲虫头晕的问题是:所有的爬行都值得吗?
请你写一个程序,对于给定的露珠坐标,计算出甲虫能喝到的最多的水量。
Input
第1行: 2个整数N和M
第2..N行:第i行1个整数,表示第i滴露珠的坐标
第2..N行:第i行1个整数,表示第i滴露珠的坐标
Output
第1行:1个整数,表示甲虫能喝到的最多的水量
Sample Input
3 15
6 -3 1
6 -3 1
Sample Output
25
Hint
【数据规模】
0 ≤ n ≤ 300,
1 ≤ m ≤ 1 000 000,
-10 000 ≤ x1, x2, . . . , xn ≤ 10 000, xi != xj for i != j。
0 ≤ n ≤ 300,
1 ≤ m ≤ 1 000 000,
-10 000 ≤ x1, x2, . . . , xn ≤ 10 000, xi != xj for i != j。
Solution
很明显,我们发现这是一个dp问题(来自于蒟蒻的废话。。。)
但是如果我们直接按照题意进行构建的话,我们会发现这里的操作是具有后效性的。我们当前浪费在
路上的时间会使得接下来获取的露珠价值发生变化,所以我们能够得出的并不是全局的最优解。
蒟蒻有一个想法:既然我们所有的露珠每一秒的蒸发量都是一样的,那么我们是不是能够把每一个点
获得的代价预处理之后加入dp呢?这看上去是难以实现的,但是对于dp问题,我们通常可以用增加维
度的方式储存并计算更多的信息。
我们假设甲虫的最优方案在取得k颗露珠存在,那么对于当前吸取第x颗露珠,就需要减去d(x)*(k-x+1)
的露珠数值,这里的f(x)是从当前位置到x的距离。我们发现,之后的损耗被我们提前处理了,我们就
可以依旧把每一颗露珠的价值当成m来进行处理了。
枚举k之后,对于具体的dp方程,我们可以发现甲虫获得露珠一定是在个连续的区间[L,R]之间。(因为
获得露珠不需要时间,所以略过某一露珠而不吸取是不经济的;-)
那么我们将Fleft[i][j]记为第i个露珠到第j个露珠全部吸取,并且最后停留在左边的最大值。相应的
我们定义Fright。
推导出转移方程:
Fleft[i][j]=m+max(Fleft[i+1][j]-(dis[i+1]-dis[i])*(k-j+i),Fright[i+1][j]-(dis[j]-dis[i])*(k-j+i));
Fright[i][j]=m+max(Fright[i][j-1]-(dis[j]-dis[j-1])*(k-j+i),Fleft[i][j-1]-(dis[j]-dis[i])*(k-j+i));
(对于当前区间的最后一个节点,有可能是从前一个点转移过来,也有可能是取完其他所有点之后从区
间的另一端转移而来。)
最后记得在dp过程之中更新最大值。
但是如果我们直接按照题意进行构建的话,我们会发现这里的操作是具有后效性的。我们当前浪费在
路上的时间会使得接下来获取的露珠价值发生变化,所以我们能够得出的并不是全局的最优解。
蒟蒻有一个想法:既然我们所有的露珠每一秒的蒸发量都是一样的,那么我们是不是能够把每一个点
获得的代价预处理之后加入dp呢?这看上去是难以实现的,但是对于dp问题,我们通常可以用增加维
度的方式储存并计算更多的信息。
我们假设甲虫的最优方案在取得k颗露珠存在,那么对于当前吸取第x颗露珠,就需要减去d(x)*(k-x+1)
的露珠数值,这里的f(x)是从当前位置到x的距离。我们发现,之后的损耗被我们提前处理了,我们就
可以依旧把每一颗露珠的价值当成m来进行处理了。
枚举k之后,对于具体的dp方程,我们可以发现甲虫获得露珠一定是在个连续的区间[L,R]之间。(因为
获得露珠不需要时间,所以略过某一露珠而不吸取是不经济的;-)
那么我们将Fleft[i][j]记为第i个露珠到第j个露珠全部吸取,并且最后停留在左边的最大值。相应的
我们定义Fright。
推导出转移方程:
Fleft[i][j]=m+max(Fleft[i+1][j]-(dis[i+1]-dis[i])*(k-j+i),Fright[i+1][j]-(dis[j]-dis[i])*(k-j+i));
Fright[i][j]=m+max(Fright[i][j-1]-(dis[j]-dis[j-1])*(k-j+i),Fleft[i][j-1]-(dis[j]-dis[i])*(k-j+i));
(对于当前区间的最后一个节点,有可能是从前一个点转移过来,也有可能是取完其他所有点之后从区
间的另一端转移而来。)
最后记得在dp过程之中更新最大值。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
char c;int rec=0,f=1;
while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec*f;
}
int ans=0;
int n,m,dis[305];
int Fleft[305][305],Fright[305][305];
int main(){
n=read();m=read();
for(int i=1;i<=n;i++)dis[i]=read();
sort(dis+1,dis+1+n);
for(int k=1;k<=n;k++){
memset(Fleft,0,sizeof(Fleft));
memset(Fright,0,sizeof(Fright));
for(int i=1;i<=n;i++){
Fleft[i][i]=Fright[i][i]=m-abs(dis[i])*k;
ans=max(ans,Fleft[i][i]);
}
for(int len=2;len<=k;len++){
for(int i=1;i<=n-len+1;i++){
int j=i+len-1;
Fleft[i][j]=m+max(Fleft[i+1][j]-(dis[i+1]-dis[i])*(k-j+i),
Fright[i+1][j]-(dis[j]-dis[i])*(k-j+i));
Fright[i][j]=m+max(Fright[i][j-1]-(dis[j]-dis[j-1])*(k-j+i),
Fleft[i][j-1]-(dis[j]-dis[i])*(k-j+i));
}
}
for(int i=1;i+k-1<=n;i++)
ans=max(ans,max(Fleft[i][i+k-1],Fright[i][i+k-1]));
}cout<<ans;
return 0;
}