分析

关于此题的 \(dp\) 思路,其实可以先去做一做这道题P1220 关路灯。两道题的思路大体相同,不过这道题有一个细节,就是露水的水分不会降为负数,因此不能一次 \(dp\) 完,因此我们考虑枚举喝到多少枚露水,以避免过程中出现某一滴露水过度消费的情况。

思路

\(dp[i][j][0/1]\) 表示喝完 \(i\)\(j\) 所有露水后所消耗的最小水分,0表示喝完后位于最左端,1表示位于最右端,以此来确定下一次 \(dp\) 甲虫所需走的时间,答案即为总水分减去消耗即可。

注意初始位置为0,若没有该点,需新加一个,特殊处理。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,qd,a[305],dp[305][305][2];

int jc(int mb){
	memset(dp,127,sizeof(dp));
	dp[qd][qd][0]=0;
	dp[qd][qd][1]=0;//初始化 
	for(int l=2;l<=mb;l++){
		for(int i=1,j=i+l-1;j<=n;j++,i++){
			dp[i][j][0]=min(dp[i][j][0],min(dp[i+1][j][0]+(a[i+1]-a[i])*(mb-l+1),dp[i+1][j][1]+(a[j]-a[i])*(mb-l+1)));
			dp[i][j][1]=min(dp[i][j][1],min(dp[i][j-1][1]+(a[j]-a[j-1])*(mb-l+1),dp[i][j-1][0]+(a[j]-a[i])*(mb-l+1)));
			//mb-l+1为喝这一滴露水前还有多少没喝,全部计入消耗 
		}
	}
	int mn=2e9;
	for(int i=1;(i+mb-1)<=n;i++){
		mn=min(mn,min(dp[i][i+mb-1][0],dp[i][i+mb-1][1]));
	}
	return mn;
}

int main()
{
	cin>>n>>m;
	int rt=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!a[i])rt=1;//是否存在位置为0的露水 
	}
	if(!rt)n++;//若没有,新添一个 
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		if(!a[i])qd=i;//确定起点 
	}
	int ans=-1;
	for(int i=1;i<=n;i++){//枚举喝水滴数 
		ans=max(ans,m*i-jc(i));
	}
	if(!rt)ans-=m;//特殊处理,加的一滴并无水分 
	cout<<ans<<endl;
	return 0;
}