「CJOJ2573」Snake vs Block


Sample Input


5
-2 0 0 1 -2
0 2 0 0 0
-4 -3 -2 -3 -7
1 0 0 0 0
0 -2 0 -2 0
0

Sample Output


8


题解

这是一道比较困难的DP,因为它分层,所以很容易想到DP,具体实现参考代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int N=201,M=10001;
int ans,roll;
int a[N][5],b[N][5],f[2][M][5],g[M][5][5];
bool bz[N][5];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
	freopen("snakevsblock.in","r",stdin);
	freopen("snakevsblock.out","w",stdout);
    int n=read(),mx=n*50;
    for(int i=1;i<=n;i++)
        for(int j=0;j<5;j++)
        {
            a[i][j]=read();
            b[i][j]=max(-a[i][j],0);//得分 
        }
    int m=read();
    while(m--)
    {
        int x=read(),y=read();
        bz[x][y-1]=true;//墙壁 
    }
    memset(f,128,sizeof(f));
    f[0][4][2]=0;//dp 
    for(int i=1;i<=n;i++)
    {
        roll^=1;
        memset(f[roll],128,sizeof(f[roll])); 
        memset(g,128,sizeof(g));
        for(int j=0;j<=mx;j++)//长度(现在) 
            for(int k=0;k<5;k++)//纵列 
            {
                int s=j-a[i][k];//表示曾经的长度 
                if(s>=0 && s<=mx) f[roll][j][k]=g[j][k][k]=f[roll^1][s][k]+b[i][k];//这里可以走 
            }
        for(int k=1;k<5;k++)//区间长 
            for(int l=0;l+k<5;l++)//左端点 
                for(int j=0;j<=mx;j++)//长度 
                {
                    int r=l+k,s=j-a[i][l];//计算右端点和曾经的长度 
                    if(!bz[i][l] && s>=0 && s<=mx) g[j][l][r]=g[s][l+1][r]+b[i][l];//从(l,r]区间变成[l,r] 
                    s=j-a[i][r];
                    if(!bz[i][r-1] && s>=0 && s<=mx) g[j][l][r]=max(g[j][l][r],g[s][l][r-1]+b[i][r]);//同上 
                    for(int p=l;p<=r;p++) f[roll][j][p]=max(f[roll][j][p],g[j][l][r]);//更新 
                }
        for(int j=0;j<=mx;j++)
            for(int k=0;k<5;k++) ans=max(ans,f[roll][j][k]);//算答案 
    }
    printf("%d",ans);
    return 0;
}
/*
因为如果算i的话,只能从上面下来,所以可以用滚动数组优化空间.
*/ 
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
using namespace std;
const int maxn=210;
int a[maxn][6],b[maxn][6],bz[maxn][6];
int f[2][10010][6],g[10010][6][6];
int main(){
//	freopen("snakevsblock.in","r",stdin);
//	freopen("snakevsblock.out","w",stdout);
	int i,j,k,n,m,ans=0;
	scanf("%d",&n);
	int lm=n*50;
	for(i=1;i<=n;i++)
		for(j=0;j<5;j++){
			scanf("%d",&a[i][j]);
			b[i][j]=max(0,-a[i][j]);
		}
	scanf("%d",&m);
	for(i=1;i<=m;i++){
		int x,y;scanf("%d%d",&x,&y);
		bz[x][y-1]=1;
	}
	memset(f,-128,sizeof(f));
	f[0][4][2]=0;
	int gd=0;
	for(i=1;i<=n;i++){
		gd^=1;
		memset(f[gd],-128,sizeof(f[gd]));
		memset(g,-128,sizeof(g));
		for(j=0;j<=lm;j++)
			for(k=0;k<5;k++){
				int sold=j-a[i][k];
				if(sold>=0 && sold<=lm)f[gd][j][k]=g[j][k][k]=f[gd^1][sold][k]+b[i][k];
			}
		for(k=1;k<5;k++)
			for(int l=0;l+k<5;l++)
				for(j=0;j<=lm;j++){
					int r=l+k,sold=j-a[i][l];
					if(!bz[i][l] && sold>=0 && sold<=lm)
						g[j][l][r]=g[sold][l+1][r]+b[i][l];
					sold=j-a[i][r];
					if(!bz[i][r-1] && sold>=0 && sold<=lm)
						g[j][l][r]=max(g[j][l][r],g[sold][l][r-1]+b[i][r]);
					for(int p=l;p<=r;p++)
						f[gd][j][p]=max(f[gd][j][p],g[j][l][r]);
				}
		for(j=0;j<=lm;j++)
			for(k=0;k<5;k++)
				ans=max(ans,f[gd][j][k]);
	}
	printf("%d
",ans);
	return 0;
} 

原文地址:https://www.cnblogs.com/cjgjh/p/9360401.html