第十三次CCF第四题 1803——04 博弈

我又写了一个简洁版的2.0; 可以作为博弈搜索树的模板  ; 

https://www.cnblogs.com/xidian-mao/p/9389974.html

    废话ps: 开始觉得这是一道简单得博弈 3*3暴力肯定可以解决问题   刚该开始得思路是直接dfs()判断谁输谁赢了,后来发现题意要输出最优解得情况(最后卡在哪好久:

还是自己太自信了: 最后五点去上了个厕所;路上想到了dfs可以返回赢的最小步数和输的最大步数这应该是最优解了-----T&T 最后没时间交了,回来补得,试了一些数据感觉没有问题,如果有bug欢迎指出,还是自己太菜了)

题目分析:

博弈——每个人采取最优策略,问最后有几个空格;

首先写一个check()判断现在得局面 谁赢谁输 平局 还是继续可以下  这个暴力枚举即可

 1 int check () {
 2     for (int i=1;i<=3;i++) {
 4         int x=mp[i][1];  
 5         if (x==0) continue;
 6         for (int j=1;j<=3;j++) {
 7             if (mp[i][j]!=x)
 8                 break;
 9             if (j==3)
10                 return x;
11         }
12     }
13      for (int i=1;i<=3;i++) {
14         int x=mp[1][i];
15         if (x==0) continue;
16         for (int j=1;j<=3;j++) {
17             if (mp[j][i]!=x)
18                 break;
19             if (j==3)
20                 return x;
21         }
22     }
23     int x=mp[1][1];
24     if (x&&mp[1][1]==mp[2][2]&&mp[2][2]==mp[3][3]) return x;
25     x=mp[2][2];
26     if (x&&mp[3][1]==mp[2][2]&&mp[2][2]==mp[1][3])  return x;
    if (num==0)  return 0;
27 return -1; 28 }

check()返回值:

0:平局

1:1赢

2: 2赢

-1:局面可以继续下

核心int  dfs(x,y,p) // 作用: 当p选手在x,y位置放下棋子时如果必赢,最少赢多少步,如果必输,最多可以输多少步;

思路转变当你放下一个棋子还不能赢得比赛胜利时,比赛得主动权已经移交给对方了

此时对方dfs()遍历搜索最优路径,只有当对方必败时,你才会赢, 只要对方有一种情况可以赢,你就要gg了

对方如果一定会赢 会选择最小赢得步数  这个路径也是你输得步数+1

如果对方一定会输 她会选择一个输得最大步数  这个步数等于你赢得步数+1;

关键点:你下完之后主动权已经在于对方 对方得最优策略才是你下完这一步得最终结果

dfs():返回值

x>0 :必赢下最下步数是x

x==0 :是平局

x<0 :必输下最多得步数

 1 int dfs (int x,int y,int t)  {
 2     mp[x][y]=t; num--;
 3     int ans=check();
 4     if (ans==t)  { mp[x][y]=0; num++; return 1; }  // 下完这一步立即获胜
 5     if (ans==0)  { mp[x][y]=0; num++; return 0; }  // 下完这一步平局  // 不存在你下完就立即输。。。。。
 6     int k=1;
 7     if (t==1)  k=2;// k 交换主动权 你下完之后得最终结果取决于对方
 8     int m1=100;//  初始化赢得最小步
 9     int m2=0;// 输得最大步 (用负数表示)
10     ans=-1;// 假设对方一定输
11     for (int i=1;i<=3;i++)       {
12         for (int j=1;j<=3;j++) {
13             if (mp[i][j]==0) {
14                 int tmp=dfs (i,j,k);
15                 ans=max (ans,tmp);// 只要对方一种情况赢, ans就大于0, t一定会输
16                 if (tmp>0)  {
17                    m1=min(m1,tmp);// 赢得最小步
18                 }
19                 if (tmp<0) {
20                     m2=min(m2,tmp);//输得最大步 (用负数表示)
21                 }
22             }
23         }
24     }
25     mp[x][y]=0;   num++;
26     if (ans==0)   return 0;
27     if (ans>0)   return -(m1+1);// 如果对方会赢 你就输这个步数取决于对方
28     return -(m2-1);//对方无论怎么走都输

完整代码   (本人qq 821474143  如果想的不周全,欢迎大家指教_


#include <bits/stdc++.h>
using namespace std;
int mp[10][10];
int num1,num2;// num1 1数目  num2 2数目
int num;// 空格数目
int n;
int check () {
    for (int i=1;i<=3;i++) {
        int x=mp[i][1];
        if (x==0) continue;
        for (int j=1;j<=3;j++) {
            if (mp[i][j]!=x)
                break;
            if (j==3)
                return x;
        }
    }
     for (int i=1;i<=3;i++) {
        int x=mp[1][i];
        if (x==0) continue;
        for (int j=1;j<=3;j++) {
            if (mp[j][i]!=x)
                break;
            if (j==3)
                return x;
        }
    }
    int x=mp[1][1];
    if (x&&mp[1][1]==mp[2][2]&&mp[2][2]==mp[3][3]) return x;
    x=mp[2][2];
    if (x&&mp[3][1]==mp[2][2]&&mp[2][2]==mp[1][3])  return x;
    if (num==0)  return 0;
    return -1;
}
int dfs (int x,int y,int t)  {
    mp[x][y]=t;  num--;
    int ans=check();
    if (ans==t)  { mp[x][y]=0; num++; return 1; }
    if (ans==0)  { mp[x][y]=0; num++; return 0; }
    int k=1;
    if (t==1)  k=2;
    int m1=100;
    int m2=0;
    ans=-1;
    for (int i=1;i<=3;i++)       {
        for (int j=1;j<=3;j++) {
            if (mp[i][j]==0) {
                int tmp=dfs (i,j,k);
                ans=max (ans,tmp);
                if (tmp>0)  {
                   m1=min(m1,tmp);
                }
                if (tmp<0) {
                    m2=min(m2,tmp);
                }
            }
        }
    }
    mp[x][y]=0;  num++;
    if (ans==0)   return 0;
    if (ans>0)   return -(m1+1);
    return -(m2-1);


}
int main ()
{
    cin>>n;
    while (n--) {
        num1=0;
        num2=0;
        for (int i=1;i<=3;i++)
            for (int j=1;j<=3;j++) {
                cin>>mp[i][j];
                if (mp[i][j]==1) num1++;
                else if (mp[i][j]==2) num2++;
            }
        num=3*3-num1-num2;
        int ans=check();
        //cout<<ans<<endl;
        if (ans==1)  cout<<num+1<<endl;
        else if (ans==2) cout<<-(num+1)<<endl;
        else if (ans==0) cout<<"0"<<endl;
        else {
            int m1=100;
            int m2=0;
            int x=2;
            if (num1==num2)  x=1;
            int ans=-1;
            for (int i=1;i<=3;i++)       {
                for (int j=1;j<=3;j++) {
                    if (mp[i][j]==0) {
                         int t=dfs (i,j,x);
                          ans=max (ans,t);
                          if (t>0) {
                            m1=min (t,m1);// 赢得最小步数
                          }
                          if (t<0) {
                            m2=min (t,m2);  //输的最大步数
                          }
                    }
                 }
            }
            if (ans>0)       
              if (x==1) cout<<num-m1+1<<endl;
              else      cout<<-(num-m1+1)<<endl;
            else if (ans==0)   cout<<"0"<<endl;
            else   
              if (x==2)  cout<<num+m2+1<<endl;
              else       cout<<-(num+m2+1)<<endl;
        }
    }
    return 0;
}


 一个典型得数据

1 0 0

0 0 0

0 0 2

电脑给出的是1赢 你能一次走对使1赢吗?真是有趣

博弈___ xdoj+1045

定义一种新的黑白棋:

1. 棋盘大小为5*5的格子;

2. 有些格子不能放棋子;

3. 同一个格子最多放一个棋子;

4. 先手执白棋,后手执黑棋;

5. 先手第一次可以把棋放在任意可以放的位置上;

6. 接下来两人轮流放棋子,这个棋子必须与上一个人放的棋子相邻

请问:两人都是最优策略,是先手赢,还是先手输?

输入

有多组输入数据,第一行为一个数字T,代表有T组输入数据 (0<T≤10)。

接下来为T组数据。

每组数据分5行、每行5个数字构成,每个数字为0或1。0表示这个位置可以放棋子,1表示这个位置不能放棋子。

输出

对于每组数据,在一行上输出“win”或“lose”,表示先手赢或输。

样例输入

2	
11111
11111
11111
11111
00000
11111
11111
11111
11111
10000

样例输出

win
lose

暴力枚举!!!
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int n=5;
 4 bool  mp[10][10];// 地图 每一格能否放
 5 bool visit[10][10];// 遍历访问
 6 int dx[]={0,0,-1,1};
 7 int dy[]={1,-1,0,0};
 8 bool dfs (int x, int y) {
 9     visit[x][y]=1;
10     int flag=1;
11     for (int i=0;i<4;i++) {
12         int tx=x+dx[i];
13         int ty=y+dy[i];
14         if (tx>=1&&tx<=n&&ty>=1&&ty<=n&&!visit[tx][ty]&&!mp[tx][ty]) {
15             int tmp=dfs (tx,ty);
16             if (tmp)  {
17                 flag=0;
18                 break;
19             }
20         }
21     }
22     visit[x][y]=0;
23     return flag;
24 }
25 int main ()
26 {
27     int T;
28     scanf ("%d",&T);
29     while (T--) {
30         for (int i=1;i<=n;i++)
31             for (int j=1;j<=n;j++) 
32                 scanf ("%1d",&mp[i][j]);
33         int flag=0;
34         for (int i=1;i<=n&&!flag;i++)// 二重循环的遍历的问题
35             for (int j=1;j<=n;j++) {
36                 memset (visit,0,sizeof(visit));
37                 if (!mp[i][j]) {
38                     flag=dfs (i,j);
39                     if (flag) break;
40                 }
41             }
42         if (flag)  printf ("win
");
43         else       printf ("lose
");
44     }
45     return 0;
46 }
 
抓住青春的尾巴。。。
原文地址:https://www.cnblogs.com/xidian-mao/p/8601683.html