【模拟试题】甲虫

【模拟试题】甲虫

Description

一只甲虫发现它呆在一根狭窄的水平树枝上。“我呆在一根狭窄的水平树枝上,”甲虫心想,“我感觉象呆在X轴上一样”(这是一只数学思维相当深刻的甲虫!)在那根树枝上还有N滴露珠,每一滴含有M单位的水分,它们的位置分别在整点坐标x1, x2, . . . , xn。甲虫的初始位置在坐标原点。 
天气很热,在一个单位时间里每滴露珠都会蒸发掉1单位的水分。甲虫很口渴,它一走到一滴露珠前就会在0单位时间内把它喝光。 
甲虫一个单位时间能爬行一个单位的长度。令甲虫头晕的问题是:所有的爬行都值得吗? 
请你写一个程序,对于给定的露珠坐标,计算出甲虫能喝到的最多的水量。

Input

第1行: 2个整数N和M 
第2..N行:第i行1个整数,表示第i滴露珠的坐标

Output

第1行:1个整数,表示甲虫能喝到的最多的水量

Sample Input

3 15
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。

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过程之中更新最大值。 
#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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值