題目 大意
一條直線上的n個點,其中第i個點位於數軸上的位置x[i]。如果從i跳到j,首先要花費時間|x[j]-x[i]|。 假如i小於j,還需額外花費時間d[i]+a[j],如果i>j,需額外花費時間c[i]+b[j]。已經到過的點無法再次到達。每次可以選擇若干個沒有到過的點,然後在其中一個點開始,按某種順序跳過所有點,最終跳回開始的點。經過若干次這樣的行動後,最多能消耗多少時間。(一次行動到下一次之間不會消耗時間)
n,m<=5000,a[i],b[i],c[i],d[i]<=1e9
時間限制2s
空間限制256M
解題思路
其實每一個點都可以根據四種不同的連邊情況把收益單獨拆分出來,然後用DP解決問題。
f[i][j][k]表示選了前i個點後,有j個出度,k個入度沒有確定。這些不確定的度都是又前i個點與後n-i個點之間的邊產生的。經過觀察後可發現,j、k是同時±1的,所以可以把f[i][j][k]變爲f[i][j];
狀態轉移方程如下:
1、f[i+1][j-1]=max(f[i+1][j-1],f[i][j]+x[i+1]+x[i+1]+a[i+1]+c[i+1]);
2、f[i+1][j]=max(f[i+1][j],f[i][j]+a[i+1]+d[i+1]);
3、f[i+1][j]=max(f[i+1][j],f[i][j]+b[i+1]+c[i+1]);
4、f[i+1][j+1]=max(f[i+1][j+1],f[i][j]-x[i+1]-x[i+1]+d[i+1]+b[i+1]);
前三條的轉移條件是j>0。
代碼如下
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 5006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
int i,j,n,x[maxn],a[maxn],b[maxn],c[maxn],d[maxn];
ll f[maxn][maxn];
int main()
{
freopen("mixedblood1.in","r",stdin);
// freopen("sec.out","w",stdout);
scanf("%d",&n);
fr(i,1,n) scanf("%d",&x[i]);
fr(i,1,n) scanf("%d",&a[i]);
fr(i,1,n) scanf("%d",&b[i]);
fr(i,1,n) scanf("%d",&c[i]);
fr(i,1,n) scanf("%d",&d[i]);
memset(f,255,sizeof(f));
f[0][0]=0;
fr(i,0,n-1)
fr(j,0,i)
if (f[i][j]!=-1)
{
if (j>0)
{
f[i+1][j-1]=max(f[i+1][j-1],f[i][j]+x[i+1]+x[i+1]+a[i+1]+c[i+1]);
if (i>0)
{
f[i+1][j]=max(f[i+1][j],f[i][j]+a[i+1]+d[i+1]);
f[i+1][j]=max(f[i+1][j],f[i][j]+b[i+1]+c[i+1]);
}
}
f[i+1][j+1]=max(f[i+1][j+1],f[i][j]-x[i+1]-x[i+1]+d[i+1]+b[i+1]);
}
printf("%lld\n",f[n][0]);
return 0;
}