记忆化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;
}
View Code

 

posted @ 2019-11-02 16:11  starve_to_death  阅读(119)  评论(0编辑  收藏  举报