题意:给一个只有三行的方块阵(横向最多100个),然后p,q,r分别代表第1,2,3层的方格数,两人轮流去掉一个格子,此时这个格子的右上方都会被去掉,面临只剩最左下角的一个格子的状态的人输,问先手能否赢,要赢得话应该取哪个方格。
解法:记忆化搜索,设dp[p][q][r]表示第1,2,3层方格数分别为p,q,r的输赢状态,0为输,1为赢,X[][][],Y[][][]分别表示其该取的方格坐标。每次求dp[p][q][r]时,枚举能达到的状态,如果这些状态的输赢值有一个为输,则此状态一定为赢,返回1,并记录好坐标,如果没有一个为输,则此状态为输。
初始值:
dp[1][0][0] = 0;
dp[1][1][0] = 1,X[1][1][0] = 1,Y[1][1][0] = 2;
dp[2][0][0] = 1,X[2][0][0] = 2,Y[2][0][0] = 1;
注意初始状态一定要最原始化,不要添加诸如只有最底层并且有n个,dp[n][0][0] = 1等的状态,因为不一定要这样取,也可能该行一个一个的取。
记忆化搜索加快速度
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> using namespace std; #define N 1007 #define M 33 int dp[104][104][104]; int X[104][104][104],Y[104][104][104]; int kx,ky; void init() { memset(dp,-1,sizeof(dp)); memset(X,0,sizeof(X)); memset(Y,0,sizeof(Y)); //dp[0][0][0] = 1; dp[1][0][0] = 0; //写出最基本的几个即可,不要乱加 dp[1][1][0] = 1,X[1][1][0] = 1,Y[1][1][0] = 2; //dp[1][1][1] = 1,X[1][1][1] = 2,Y[1][1][1] = 1; //for(int i=2;i<=100;i++) dp[2][0][0] = 1,X[2][0][0] = 2,Y[2][0][0] = 1; } int DP(int p,int q,int r) { if(dp[p][q][r] != -1) return dp[p][q][r]; int i; for(i=r;i>=1;i--) //顶层 { if(DP(p,q,i-1) == 0) { X[p][q][r] = i; Y[p][q][r] = 3; return dp[p][q][r] = 1; } } for(i=q;i>=1;i--) //中层 { if(DP(p,i-1,min(r,i-1)) == 0) { X[p][q][r] = i; Y[p][q][r] = 2; return dp[p][q][r] = 1; } } for(i=p;i>=2;i--) //底层,注意>=2而不是1 { if(DP(i-1,min(q,i-1),min(r,i-1)) == 0) { X[p][q][r] = i; Y[p][q][r] = 1; return dp[p][q][r] = 1; } } return dp[p][q][r] = 0; } int main() { int p,q,r,t,cs; scanf("%d",&t); init(); while(t--) { scanf("%d%d%d%d",&cs,&p,&q,&r); if(DP(p,q,r)) printf("%d W %d %d\n",cs,X[p][q][r],Y[p][q][r]); else printf("%d L\n",cs); } return 0; }