Atcoder abc の 初体験

[ABC320F] Fuel Round Trip

若只考虑单程的,设一个 \(dp_{i,j}\) 表示走到第 \(i\) 个加油站剩下的油为 \(j\) 的最小花费。

但是还需要考虑回来可能也要加油,所以综合考虑,设 \(dp_{i,j,k}\) 表示到第 \(i\) 个加油站去的途中有 \(j\) 的油量,归途有 \(k\) 的油量的最小花费。

考虑 \(dp_{i,j,k}\) 的转移,设 \(d=s_{i+1}-s_{i}\)

若来回都不加,则 \(dp_{i+1,j-d,k+d}=min(dp_{i,j,k})\);

如果去的时候加,则 \(dp_{i+1,min(j+f_i,H)-d,k+d}=min(dp_{i,j,k}+p_i)\)

如果回的时候加,设下一个加油站时回时油量为 \(x\) ,则 \(min(x-d+f_i,H)=k\)\(k<H\)\(x=k+len-f_i\) ,若 \(k=H\)\(x \geq H+d-f_i\) 转移就是 \(dp_{i+1,j-d,x}=min(dp_{i,j,k}+p_i)\)

注意转移时第二维不要小于零,如果加油,转移的油量一定要大于加的油量。

复杂度 \(O(n*h^2)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=500;
int s[N];
int p[N],f[N];
int dp[N][N][N];
const int inf=2139062143;
signed main(){
	int n,h;
	scanf("%d%d",&n,&h);
	int ma=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&s[i]);
		ma=max(ma,s[i]-s[i-1]);
	}
	if(ma>h){
		cout<<"-1";
		return 0;
	}
	for(int i=1;i<n;i++) scanf("%d%d",&p[i],&f[i]);
	memset(dp,0x7f,sizeof(dp));
	for(int i=0;i<=h;i++){
		dp[0][h][i]=0;
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<=h;j++){
			for(int k=0;k<=h;k++){
				if(dp[i][j][k]==inf) continue;
				int len=s[i+1]-s[i];
				if(j-len>=0 && k+len<=h){
					dp[i+1][j-len][k+len]=min(dp[i+1][j-len][k+len],dp[i][j][k]);
				}
				if(min(j+f[i],h)-len>=0 && k+len<=h){
					dp[i+1][min(j+f[i],h)-len][k+len]=min(dp[i+1][min(j+f[i],h)-len][k+len],dp[i][j][k]+p[i]);
				}
				if(k<h){
					if(j-len>=0 && k-f[i]+len<=h && k>=f[i]){
						dp[i+1][j-len][k-f[i]+len]=min(dp[i+1][j-len][k-f[i]+len],dp[i][j][k]+p[i]);
					}
				}
				else{
					if(j-len>=0 && k>=f[i]){
						for(int e=h+len-f[i];e<=h;e++){
							dp[i+1][j-len][e]=min(dp[i+1][j-len][e],dp[i][j][k]+p[i]);
						}
					}
				}
			}
		}
	}
	int ans=inf;
	for(int i=0;i<=h;i++){
		ans=min(ans,dp[n][i][i]);
	}
	if(ans==inf) cout<<"-1";
	else cout<<ans<<endl;
}
posted @ 2023-09-17 10:59  _bloss  阅读(86)  评论(5)    收藏  举报