【挖坑記】JZOJ 4707 艾比索特

題目 大意

一條直線上的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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章