Ali and Baba
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2172 Accepted Submission(s): 460
Problem Description
There is a rectangle area (with N rows and M columns) in front of Ali and Baba, each grid might be one of the following:
1. Empty area, represented by an integer 0.
2. A Stone, represented by an integer x (x > 0) which denote the HP of this stone.
3. Treasure, represented by an integer -1.
Now, Ali and Baba get the map of this mysterious area, and play the following game:
Ali and Baba play alternately, with Ali starting. In each turn, the player will choose a stone that he can touch and hit it. After this operation, the HP of the stone that been hit will decrease by 1. If some stone’s HP is decreased to 0, it will become an
empty area. Here, a player can touch a stone means
there is path consist of empty area from the outside to the stone. Note that two grids are adjacent if and only if they share an edge.
The player who hits the treasure first wins the game.
Input
The input consists several testcases.
The first line contains two integer N and M (0 < N,M <= 300), the size of the maze.
The following N lines each contains M integers (less than 100), describes the maze, where a positive integer represents the HP of a stone, 0 reperents an empty area, and -1 reperents the treasure.
There is only one grid contains the treasure in the maze.
Output
“Ali Win” or “Baba Win” indicates the winner of the game.
Sample Input
3 3
1 1 1
1 -1 1
1 1 1
Sample Output
Baba Win
Source
2011 Alibaba-Cup Campus Contest
题目大意:给你一个n*m的图,图内有三种值分别代表:
1、0 代表空地
2、-1代表宝藏
3、1-100表示石头的血量值。
对于游戏的规则如下:谁先拿到宝藏谁赢,Ali先手,对于Ali和Baba来说,只能向上下左右四个方向走,每人每个回合必须攻击一个石头并让其减少一个血量,也就是说,如果有一条路从边缘任意(x==1||x==n||y==1||y==m)一点出发,如果能够走到-1,那么就相当于赢了。
思路:
(这个题真的是需要多方向多方面多角度来考虑的一个题,真的是很好很好很好很好的一个题。)
1、首先,如果有一条路能够从边缘一点出发,直接有一条路径能够保证不遇到石头的情况下直接走到宝藏上,因为Ali先手,所以Ali这种情况下就赢了,不需要继续判断了。这个问题我们可以直接从宝藏开始广搜,如果走到了边缘,就属于满足情况,这部分还是比较好实现的。
2、
如果不满足刚才所诉的情况辣么我们需要继续思考。
我们的目的其实很简单,目标也很明确,那就是来寻找一些石头,使得干掉这些个石头之后剩下的石头里边干掉任何一个都会使得一方胜利的情况。详细点说,对于样例来讲,这些需要找到的点(石头)是(1,1)(1,3)(3,1)(3,3),然后对这些点进行奇偶判定即可。如果是偶数,那么Baba会赢,否则Ali会赢。
然后我们就朝向这个目标来继续思考。
我们需要找到这些个目标点,首先要明确一个界限,这个界限就是将宝藏围住的一个距离宝藏最近的那个石头圈。
例如这个样例:
7 7 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 1 1 1 0 1 1 0 1 -1 1 0 1 1 0 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1
明显图里边有两个石头圈,我们要找的石头圈是最里边这一圈,辣么最外边那一圈那些店都是目标点。
辣么也不难理解:这些目标点是在这个界限之外的点,所以如果界限之内有石头,这些石头其实就是无关石头
如这种样例:
7 7 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 1 1 0 1 1 0 1 1 3 0 1 1 0 0 1 0 0 1 1 0 0 -1 0 0 1 1 1 1 1 1 1 1
明显宝藏上边的六个点就是刚刚所说的无关点。
这样我们就能确定出三种石头点和一个界限。
三种石头点:
一种是界限上的石头点(有一部分界限上的石头也属于目标点),一种是界限外的石头点,也就是我们所说的目标点,另外一种石头点,就是界限内的无关点。
确定了大体内容,我们也就明确了做题的大方向:找到界限,抛掉无关点,加和目标点。
目标点=界限外的石头点血量值加和+界限上石头点大于1的部分、
说到这里可能大家的思路也是比较清晰的了,这里再综合性的写一下:
先看看先手的Ali能否直接找到宝藏,如果可以,直接退出,如果不可以,继续判断,继续判断的目标:找到目标石头点,使得最后剩下的石头圈将宝藏整好围起来,而且这个时候界限圈上的点的石头血量值都为1.所以目标点=界限外的石头点血量值加和+界限上石头点大于1的部分
然后我们分块来写代码:
1、确定界限圈,并判断是否先手的Ali能否直接找到宝藏:
void Bfs(int x,int y) { memset(vis,0,sizeof(vis)); now.x=x; now.y=y; vis[x][y]=1; queue<zuobiao >s; s.push(now); while(!s.empty()) { now=s.front(); if(now.x==1||now.x==n||now.y==1||now.y==m)//如果先手Ali能够直接找到宝藏 { ok=1;//标记上 return ; } s.pop(); for(int i=0;i<4;i++) { nex.x=now.x+fx[i]; nex.y=now.y+fy[i]; if(nex.x>=1&&nex.x<=n&&nex.y>=1&&nex.y<=m&&vis[nex.x][nex.y]==0)//如果在走的范围内,并且不重复走 { vis[nex.x][nex.y]=1;//标记上这些点都走过了,就是说这些点属于界限圈内,包括界限圈 if(a[nex.x][nex.y]==0)//如果走到的点是0,就可以继续走。 { s.push(nex); } } } } return ; }
这样,vis【x】【y】=1的点,就是在界限圈内的点,同时也确定了界限圈、
2、对目标点进行统计值加和:
这里注意一个点:对界限上的点千万不要多次加和。界限外的点一定要标记完全。
void Bfs2(int x,int y)//<span style="font-family: Arial, Helvetica, sans-serif;">在图外边加一圈值为0的隐式图</span> { memset(vis2,0,sizeof(vis2)); now.x=x; now.y=y; queue<zuobiao >s; s.push(now); vis2[x][y]=1; while(!s.empty()) { now=s.front(); s.pop(); for(int i=0;i<4;i++) { nex.x=now.x+fx[i]; nex.y=now.y+fy[i]; if(nex.x>=0&&nex.x<=n+1&&nex.y>=0&&nex.y<=m+1&&vis2[nex.x][nex.y]==0&&vis2[nex.x][nex.y]!=2)//能在走的范围内走,并且不重复走一个点,另外要注意的一个点是不要对界限圈上的点重复走 { if(vis[nex.x][nex.y]==0)//界限圈外的点 { vis2[nex.x][nex.y]=1; s.push(nex); } if(vis[nex.x][nex.y]==1)//界限圈上的点 { vis2[nex.x][nex.y]=2; sum+=a[nex.x][nex.y]-1;//这块特殊处理一下,使得界限圈上的石头点血量值变成1, } } } } for(int i=0;i<=n+1;i++) { for(int j=0;j<=m+1;j++) { if(vis2[i][j]==1)sum+=a[i][j];//界限外的点进行加和 } } }
最后是完整的AC代码:
#include<stdio.h> #include<queue> #include<string.h> using namespace std; struct zuobiao { int x,y; }now,nex; int a[505][505]; int vis[505][505]; int vis2[505][505]; int fx[4]={0,0,1,-1}; int fy[4]={1,-1,0,0}; int n,m,ok,sum; void Bfs(int x,int y) { memset(vis,0,sizeof(vis)); now.x=x; now.y=y; vis[x][y]=1; queue<zuobiao >s; s.push(now); while(!s.empty()) { now=s.front(); if(now.x==1||now.x==n||now.y==1||now.y==m) { ok=1; return ; } s.pop(); for(int i=0;i<4;i++) { nex.x=now.x+fx[i]; nex.y=now.y+fy[i]; if(nex.x>=1&&nex.x<=n&&nex.y>=1&&nex.y<=m&&vis[nex.x][nex.y]==0) { vis[nex.x][nex.y]=1; if(a[nex.x][nex.y]==0) { s.push(nex); } } } } return ; } void Bfs2(int x,int y) { memset(vis2,0,sizeof(vis2)); now.x=x; now.y=y; queue<zuobiao >s; s.push(now); vis2[x][y]=1; while(!s.empty()) { now=s.front(); s.pop(); for(int i=0;i<4;i++) { nex.x=now.x+fx[i]; nex.y=now.y+fy[i]; if(nex.x>=0&&nex.x<=n+1&&nex.y>=0&&nex.y<=m+1&&vis2[nex.x][nex.y]==0&&vis2[nex.x][nex.y]!=2) { if(vis[nex.x][nex.y]==0) { vis2[nex.x][nex.y]=1; s.push(nex); } if(vis[nex.x][nex.y]==1) { vis2[nex.x][nex.y]=2; sum+=a[nex.x][nex.y]-1; } } } } for(int i=0;i<=n+1;i++) { for(int j=0;j<=m+1;j++) { if(vis2[i][j]==1)sum+=a[i][j]; } } } int main() { while(~scanf("%d%d",&n,&m)) { int sx,sy; memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); if(a[i][j]==-1) { sx=i; sy=j; } } } ok=0; sum=0; Bfs(sx,sy); if(ok==1) { printf("Ali Win\n"); continue; } Bfs2(0,0); if(sum%2==0) { printf("Baba Win\n"); } else printf("Ali Win\n"); } }