分析
关于此题的 \(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;
}