【挖坑记】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
#include
#include
#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;
}

你可能感兴趣的:(挖坑记)