HDU 5754 Life Winner Bo(博弈论+dp)

202 篇文章 1 订阅
38 篇文章 1 订阅

Description
B和G在一个n*m的棋盘上玩游戏,游戏规则如下:初始状态棋子在(1,1)处,两人轮流移动棋子(B先手),谁先将棋子移到(n,m)谁赢,如果棋子不在(n,m)处且无法移动则平局,有四种棋子(1国王,2车,3马,4皇后),其移动规则与国际象棋相同,现在给出棋子类型和棋盘规模,问B是否有必胜策略,假设两人都足够机智
Input
第一行一整数T表示用例组数,对于每组用例,输入三个整数type,n,m分别表示棋子类型以及棋盘规模(T<=1000,2<=n,m<=1000,1<=type<=4)
Output
对于每组用例,如果B有必胜策略则输出B,若B无论如何都必败则输出G,若平局则输出D
Sample Input
4
1 5 5
2 5 5
3 5 5
4 5 5
Sample Output
G
G
D
B
Solution
假设终点在(1000,1000),根据所有后继状态均必胜则必败以及后继状态存在必败态则必胜,就可以从终点开始转移状态得到所有的dp[n][m](0表示必败,1表示必胜,-1表示平局),对于每次查询,根据dp[1000-n+1][1000-m+1]来判断输赢即可,下面看如何转移:
国王:(i,j)有三种后继状态(i,j+1),(i+1,j),(i+1,j+1),初始化dp[1000][i]和dp[i][1000](i为偶则必败,i为奇则必胜),之后i和j都从大往小转移即可
车:(i,j)有1000-i+1000-j个后继状态,用x[i]和y[j]表示当前列第i~1000行中是否出现了1,以及当前行第j~1000列中是否出现了1,如果x[i]和y[j]都为0说明所有后继状态均是必败态,否则是必胜态
马:(i,j)有两种后继状态(i+1,j+2)和(i+2,j+1),dp[1000][i],dp[999][i],dp[i][999],dp[i][1000]这两行两列除了dp[999][998]和dp[998][999]是必胜外其余状态均是平局,之后i和j从大到小转移,如果两个后继都是1则必败,如果两个后继中出现0则是必胜,如果两个后继都是平级则是平局
皇后:(i,j)有1000-i+1000-j+min(1000-i,1000-j)个后继状态,用x[i]和y[j],z[i-j+1000]分别表示当前列第i~1000行中是否出现了1,当前行第j~1000列中是否出现了1,以及当前以(i,j)为左上角的对角线上是否出现了1,如果x[i],y[j],z[i-j+1000]都为0说明所有后继状态均是必败态,否则是必胜态
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 1111
int dp1[maxn][maxn],dp2[maxn][maxn],dp3[maxn][maxn],dp4[maxn][maxn],x[maxn],y[maxn],z[2*maxn];
void init()
{
    memset(dp1,0,sizeof(dp1));
    for(int i=1;i<=1000;i++)
        if(i%2==0)dp1[1000][i]=dp1[i][1000]=0;
        else dp1[1000][i]=dp1[i][1000]=1;
    for(int i=999;i>=1;i--)
        for(int j=999;j>=1;j--)
            if(dp1[i+1][j]+dp1[i][j+1]+dp1[i+1][j+1]==3)dp1[i][j]=0;
            else dp1[i][j]=1;
    memset(dp2,0,sizeof(dp2));
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    x[1000]=y[1000]=1;
    for(int i=1;i<=1000;i++)
        dp2[1000][i]=dp2[i][1000]=1;
    for(int i=999;i>=1;i--)
        for(int j=999;j>=1;j--)
            if(!x[i]&&!y[j])x[i]=y[j]=1,dp2[i][j]=0;
            else dp2[i][j]=1;
    memset(dp3,0,sizeof(dp3));
    for(int i=1;i<=1000;i++)
        dp3[1000][i]=dp3[999][i]=dp3[i][1000]=dp3[i][999]=-1;
    dp3[999][998]=dp3[998][999]=1;
    for(int i=998;i>=1;i--)
        for(int j=998;j>=1;j--)
            if(dp3[i+1][j+2]==1&&dp3[i+2][j+1]==1)dp3[i][j]=0;
            else if(dp3[i+1][j+2]==0||dp3[i+2][j+1]==0)dp3[i][j]=1;
            else dp3[i][j]=-1;
    memset(dp4,0,sizeof(dp4));
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    memset(z,0,sizeof(z));
    x[1000]=y[1000]=z[1000]=1;
    for(int i=1;i<1000;i++)dp4[1000][i]=1;
    for(int i=999;i>=1;i--)
        for(int j=1000;j>=1;j--)
            if(!x[i]&&!y[j]&&!z[i-j+1000])
                x[i]=y[j]=z[i-j+1000]=1,dp4[i][j]=0;
            else dp4[i][j]=1; 
}
int main()
{
    init();
    int T,type,n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&type,&n,&m);
        if(type==1)printf("%c\n",dp1[1000-n+1][1000-m+1]==1?'B':'G');
        else if(type==2)printf("%c\n",dp2[1000-n+1][1000-m+1]==1?'B':'G');
        else if(type==3)
        {
            if(dp3[1000-n+1][1000-m+1]==-1)printf("D\n");
            else if(dp3[1000-n+1][1000-m+1]==0)printf("G\n");
            else printf("B\n");
        }
        else printf("%c\n",dp4[1000-n+1][1000-m+1]==1?'B':'G');
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值