ABC 320F Fuel Round Trip

Linear_L發表於2024-06-12

題意
在座標軸上給定N個點,座標依次為x1,x2,...,xn,你需要從原點前往xn並且實現往返,其中從第一個點到第N-1個點上有加油站,其中第i個加油站可以花費p[i]購買f[i]升汽油,汽油的上限為H升,每行駛一單位距離需要花費一升汽油。在全部過程中每個加油站最多使用一次,判斷是否可以完成行程並輸出最小花費。(1<=N,H<=300)

思路
考慮dp,我們先把反向行駛的過程轉化成正向行駛的過程。並設dp[i][j][k]為到達第i個站點,第一次經過時還持有j升油,第二次經過時還持有k升油時的最小花費。
那麼假設下一站為第i+1個站點,那麼從第i個站點到達第i+1個站點就有三種轉移:

  1. 第i個站點不選擇加油
  2. 第i個站點去程加油
  3. 第i個站點返程加油

設dis為第i+1個站點與第i個站點的距離那麼對應的狀態轉移方程就是:

  1. dp[i+1][j-dis][k-dis]=min(dp[i+1][j-dis][k-dis],dp[i][j][k]),(j>=dis&&k>=dis)
  2. dp[i+1][min(h,j+f[i])-dis][k-dis]=min(dp[i+1][min(h,j+f[i])-dis][k-dis],dp[i][j][k]+p[i]),(min(h,j+f[i])>=dis&&k>=dis)
  3. dp[i+1][j-dis][min(k+f[i],h)-dis]=min(dp[i+1][j-dis][min(h,k+f[i])-dis],dp[i][j][k]+p[i]),
    (min(h,k+f[i])>=dis&&j>=dis)
    接下來我們要考慮一個細節:
    我們強行將返程的過程視為正向的過程,若我們去程到達xn時剩下的油量位h-i(即去程花費了i升油),且我們在返程的過程中是將油量視為H開始的。那麼返程時的油量j必須要保證大於或等於h-i,否則就無法到達。

程式碼

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn=305;
int p[maxn],f[maxn],x[maxn];
int dp[maxn][maxn][maxn];
const int inf=1e9;

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n,h;
	cin>>n>>h;
	for(int i=1;i<=n;i++) cin>>x[i];
	for(int i=1;i<=n-1;i++) cin>>p[i]>>f[i];
	for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) for(int k=0;k<maxn;k++) dp[i][j][k]=inf;
	dp[0][h][h]=0;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<=h;j++)
		  for(int k=0;k<=h;k++)
		  {
		  	 int dis=x[i+1]-x[i];
		  	 if(j>=dis&&k>=dis)
		  	   dp[i+1][j-dis][k-dis]=min(dp[i+1][j-dis][k-dis],dp[i][j][k]);
		  	 if(min(j+f[i],h)>=dis&&k>=dis)
		  	   dp[i+1][min(j+f[i],h)-dis][k-dis]=min(dp[i+1][min(j+f[i],h)-dis][k-dis],dp[i][j][k]+p[i]);
		  	 if(j>=dis&&min(h,k+f[i])>=dis)
		  	   dp[i+1][j-dis][min(h,k+f[i])-dis]=min(dp[i+1][j-dis][min(h,k+f[i])-dis],dp[i][j][k]+p[i]);
		  }
	}
	int ans=1e9;
	for(int i=0;i<=h;i++)
	  for(int j=0;j<=h;j++)
	    if(j>=h-i) ans=min(ans,dp[n][i][j]);
	if(ans==1e9) ans=-1;
	cout<<ans<<endl;
	
	return 0;
}

相關文章