记忆化dp博弈
题:http://poj.org/problem?id=2068
题意:
有两个队伍A,B,每个队伍有N个人,交叉坐。即是A(1,3,5,7.....)B(2,4,6,8....)。告诉你每个mi(1<=i<=2n)。
现在有一堆个数为S的石堆,从第1个人开始拿石头,因为是交叉坐所以也就相当于两队轮流拿石头。注意:第i个人拿石头最少拿1个最多拿mi个,拿完石堆中最后一个石头的输。问:A队有没有必胜策略?
分析:
嗯因为数据都不是很大,我们可以用记忆化搜索来求出整个博弈图。如果后继状态有后手必胜(0)那么该状态为先手必胜(1)。如果后继状态全部为先手必胜(1).那么该点状态为后手必胜(0)。当石堆个数为0的时候状态为先手必胜。也就是图的边界。这样用回溯就可以求出所有状态是先手必胜还是后手必胜。
状态表示为 DP[ i ][ j ] 表示轮到第j个人拿,当前还有 i 个石头。
#include<bits/stdc++.h> using namespace std; const int M=1e4+4; int dp[M][30]; int a[M]; int n; int s; int dfs(int m,int p){ if(p==2*n)///循环重新来 return dfs(m,0); if(dp[m][p]!=-1)///记忆化搜索 return dp[m][p]; if(m==0)///达到该状态的人必胜态 return dp[m][p]=1; for(int i=1;i<=a[p];i++){ if(m<i) break; if(dfs(m-i,p+1)==0)///由必败态可转化为必胜态 return dp[m][p]=1; } return dp[m][p]=0;///到这里说明之前全都是必胜态。 } int main(){ while(~scanf("%d",&n)&&n){ scanf("%d",&s); for(int i=0;i<2*n;i++) scanf("%d",&a[i]); memset(dp,-1,sizeof(dp)); printf("%d\n",dfs(s,0)); } return 0; }