【蓝桥杯】2016初赛 取球博弈 dfs/dp

题目描述

两个人玩取球的游戏。一共有N个球,每人轮流取球,每次可取集合{n1,n2,n3}中的任何一个数目。
如果无法继续取球,则游戏结束。此时,持有奇数个球的一方获胜。如果两人都是奇数,则为平局。
假设双方都采用最聪明的取法,第一个取球的人一定能赢吗?试编程解决这个问题。
输入
输入存在多组测试样例,对于每一组测试数据:
第一行3个正整数n1 n2 n3,空格分开,表示每次可取的数目 (0<n1,n2,n3<100)
第二行5个正整数x1 x2 … x5,空格分开,表示5局的初始球数(0<xi<1000)
输出
一行5个字符,空格分开。分别表示每局先取球的人能否获胜。
能获胜则输出+,次之,如有办法逼平对手,输出0,无论如何都会输,则输出-

样例输入

1 2 3
1 2 3 4 5
1 4 5
10 11 12 13 15
2 3 5
7 8 9 10 11

样例输出

+0 + 0 -
0 - 0 + +
+0 0 0 0

【想说的】1.很容易被“每个人都采用最聪明的方法”给困惑住,实际上可以理解为每次都为自己找赢面,没找到就去找平局的情况,否则输掉。
2.为了减少时间复杂度,用dp记录每个局面的情况,主要记录轮到谁取球,A有多少,B有多少,注意:球数变动的时候dp要重新更新,不同的球数中dp的意义都不相同。
3.dfs递归去遍历所有情况

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n[4];//取球数组
int q[6];//5组球数
char r[6];//输出的符号
int dp[2][1003][1003];//dp【谁取】【先手多少球】【后手多少球】
// 1 先手胜  2 平局  3先手输
int dfs(int all,int x,int h,int w)
{
    int sy=all-x-h;
    if(dp[w][x][h])
    {
            return dp[w][x][h];
    }

    if(sy<n[0])
    {

        if(x%2==1&&h%2==0)
            return dp[w][x][h]=1;
        else if(x%2==0&&h%2==1)
            return dp[w][x][h]=3;
        return dp[w][x][h]=2;
    }

    if(w==1)//先手取球的时候
    {
        int p=0;//当没赢的时候看看会不会平局的记号
         for(int i=2;i>=0;i--)//从多的开始取球
        {

            if(sy<n[i])
                continue;
            int res=dfs(all,x+n[i],h,0);
            if(res==1)
                return dp[w][x][h]=1;
            if(res==2)
               p=1;

        }
        if(p==1)
            return dp[w][x][h]=2;
        return dp[w][x][h]=3;
    }
     if(w==0)//后手取球的时候
    {
        int p=0;
         for(int i=2;i>=0;i--)
        {
            if(sy<n[i])
                continue;
            int res=dfs(all,x,h+n[i],1);
            if(res==3)
                return dp[w][x][h]=3;
            if(res==2)
               p=1;

        }
        if(p==1)
            return dp[w][x][h]=2;
        return dp[w][x][h]=1;
    }

}
int main()
{
    while(~scanf("%d",&n[0]))
    {

        scanf("%d%d",&n[1],&n[2]);
        sort(n,n+3);

        for(int i=0;i<5;i++)
        {
            scanf("%d",&q[i]);
        }

        for(int i=0;i<5;i++)
        {
             memset(dp,0,sizeof(dp));
            int tem=dfs(q[i],0,0,1);


            if(tem==1)
                r[i]='+';
            else if(tem==2)
                r[i]='0';
            else if(tem==3)
                r[i]='-';
        }
        for(int i=0;i<5;i++)
        {
            printf("%c ",r[i]);
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值