我又写了一个简洁版的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\n"); 43 else printf ("lose\n"); 44 } 45 return 0; 46 }