多校联合第8场1003Mine

来源:互联网 发布:暗黑2优化的高清补丁 编辑:程序博客网 时间:2024/05/22 13:12

       Mine这题对于了解博弈论SG值的人来说,应该是能愉快的解决。我也是最近才开始学习博弈论,这里贴个地址http://www.cnitblog.com/weiweibbs/category/7163.html,我觉得对新手还是能有比较大的帮助的。回到正题。

      Mine题意大概是对扫雷游戏,给你雷的坐标,两个人轮流点,不能点炸弹,最后谁不能点谁就输了。对于这个问题,可以这样想,点击的若是有数字的,就只能点开那个数字块,如果点了空白的地方,则会打开一片区域,而这些区域的边界都是数字(如果存在的话),所以我们就可以把这个问题抽象到取石子游戏中了,首先我们可以通过BFS将棋盘分为几个区域(对应几堆石子)这样的话,对每堆石子我们可采取的策略就是取1颗或者是全部取走(Nim游戏)。

所以现在就是要推出每堆石子的SG值,最后把SG值异或一下就是,0先手必败,非0先手必胜。


       这里可以推导出SG值的一个规律,石子数为n=0 SG=0,石子数为n=1 SG=1,n=2 SG=2,n=3 SG=1 ,n=4 SG=2........可以知道n为奇数时SG=1,n为非零偶数SG=2,所以在分区时统计出每一块区域的石子个数(这里把空白区域算作1颗石子)就行,下面是我AC的代码,在这里注明,这个代码也是在参考了结题报告和网上大牛们的代码之后写出来的,如有雷同,不胜荣幸。。

#include<iostream>#include <cstdio>#include <cstring>#include <queue>#include <fstream>using namespace std;#define maxn 1010int dir[8][2]={{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};bool bomb[maxn][maxn],visit[maxn][maxn];queue<int>xpoint;queue<int>ypoint;int n,m,k;//检查i ,j 周围是否有炸弹bool check (int x,int y){if(x>0&&bomb[x-1][y]) return true;if(x<n-1&&bomb[x+1][y]) return true;if(y>0&&bomb[x][y-1]) return true;if(y<m-1&&bomb[x][y+1]) return true;if(x>0&&y>0&&bomb[x-1][y-1]) return true;if(x>0&&y<m-1&&bomb[x-1][y+1]) return true;if(x<n-1&&y>0&&bomb[x+1][y-1]) return true;if(x<n-1&&y<m-1&&bomb[x+1][y+1]) return true;return false;}int bfs(int x,int y){xpoint.push(x);ypoint.push(y);visit[x][y]=1;int sum=0;//记录每一块的石子个数while(!xpoint.empty()){int nx=xpoint.front();int ny=ypoint.front();xpoint.pop();ypoint.pop();if(check(nx,ny)){sum++;continue;}for(int i=0;i<8;i++){int px=nx+dir[i][0];int py=ny+dir[i][1];if(px<0||px>n-1||py<0||py>m-1) continue;if(!visit[px][py]&&!bomb[px][py]){xpoint.push(px);ypoint.push(py);visit[px][py]=1;}}}return sum;}int main(){int cas,ncase,i,j,ans;//ofstream out("out.txt",ios::out);//ifstream in("data.in",ios::in);scanf("%d",&ncase);//in>>ncase;for(int cas=1;cas<=ncase;cas++){scanf("%d%d%d",&n,&m,&k);//in>>n>>m>>k;memset(visit,0,sizeof(visit));memset(bomb,0,sizeof(bomb));while(!xpoint.empty()) xpoint.pop();while(!ypoint.empty()) ypoint.pop();int x,y;while(k--){scanf("%d%d",&x,&y);//in>>x>>y;bomb[x][y]=1;//表示此处有炸弹}ans=0;for(i=0;i<n;i++)for(j=0;j<m;j++){if(!visit[i][j]&&!bomb[i][j]&&!check(i,j)){int cnt=bfs(i,j);ans^=(cnt%2+1);//加1是因为空白块要加1}}for(i=0;i<n;i++)for(j=0;j<m;j++){if(!visit[i][j]&&!bomb[i][j]&&check(i,j))//单独的数字块SG值为1ans^=1;}printf("Case #%d: ",cas);if(ans==0) printf("Fanglaoshi\n");//out<<"Case #"<<cas<<": "<<"Fanglaoshi\n";else printf("Xiemao\n");//out<<"Case #"<<cas<<": "<<"Xiemao\n";}return 0;}


原创粉丝点击