连连看
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 41358 Accepted Submission(s): 10221
Problem Description
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
Input
输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。
注意:询问之间无先后关系,都是针对当前状态的!
Output
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。
Sample Input
3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0
Sample Output
YES
NO
NO
NO
NO
YES
考虑广搜:从起点s出发,判断能否到达终点e,能到达,则输出YES;否则输出NO
剪枝部分:
1.若当前点已有棋子(即map[i][j]!=0),继续下一层循环
2.已被访问的节点用f数组标记并不再访问
3.转弯次数。转弯次数用turn统计,点当前的方向用di标记。默认起点转弯次数和方向都为-1,此后每个点的方向为dir数组中的i值,若当前点的方向与上一个点的方向不同,则转弯次数加1。当转弯次数大于2时,剪枝。
注:此题用广搜存在严重bug,建议深搜。
bug简要:如上图测试数据,当从(3,4)->(1,1)时,路径为(3,4),(2,4),(2,3),(2,2),(2,1),(1,1)输出YES;但从(1,1)到(3,4)时,路径为(1,1),(1,2),(2,2),(2,3),(2,4),(3,4),由于转弯次数为3次,输出NO,而正确路径应为(1,1),(2,1),(2,2),(2,3),(2,4),(3,4),输出YES
可能是出题人考虑到了这个问题也说不定233
下面附上AC广搜代码
//time = 61ms
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
struct Node {
int x;
int y;
int turn;
int dir;
bool operator ==( const Node bb ) {
return x==bb.x && y==bb.y;
}
};
int n,m;
int dir[4][2] = { { -1,0 } , { 0,1 } , { 1,0 } , { 0,-1 } };
bool f[1010][1010];
int map[1010][1010];
bool bfs( Node s , Node e ) {
queue<Node> q;
memset( f,false,sizeof(f) );
q.push(s);
f[s.x][s.y] = 1;
Node r,p;
while( !q.empty() ) {
r = q.front();
q.pop();
for( int i=0 ; i<4 ; i++ ) {
p.x = r.x + dir[i][0];
p.y = r.y + dir[i][1];
p.dir = i;
if( r.dir==-1 )
p.turn = 0;
else
p.turn = r.dir==i ? r.turn : r.turn+1;
if( p.turn>2 || p.x<=0 || p.y<=0 || p.x>n || p.y>m || f[p.x][p.y] )
continue;
if( p==e ) {
return true;
}
if( map[p.x][p.y]==0 ) {
q.push(p);
f[p.x][p.y] = 1;
}
}
}
return false;
}
int main() {
// freopen( "in.txt","r",stdin );
std::ios::sync_with_stdio( false );
int t;
Node s,e;
while( cin >> n >> m && n && m ) {
for( int i=1 ; i<=n ; i++ ) {
for( int j=1 ; j<=m ; j++ ) {
cin >> map[i][j];
}
}
cin >> t;
while( t-- ) {
cin >> s.x >> s.y >> e.x >> e.y;
if( map[s.x][s.y]!=map[e.x][e.y] || map[s.x][s.y]==0 || map[e.x][e.y]==0 )
goto PN;
s.turn = s.dir = -1;
if( bfs( s,e ) ) {
cout << "YES" << endl;
continue;
}
PN:
cout << "NO" << endl;
}
}
}
深搜代码,思路基本相同
//time = 4024ms
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct Node {
int x;
int y;
int turn;
int dir;
bool operator ==( const Node bb ) {
return x==bb.x && y==bb.y;
}
};
int n,m;
int dir[4][2] = { { -1,0 } , { 0,1 } , { 1,0 } , { 0,-1 } };
bool f[1010][1010];
int map[1010][1010];
bool dfs( Node s , Node e ) {
f[s.x][s.y] = 1;
Node p;
for( int i=0 ; i<4 ; i++ ) {
p.x = s.x + dir[i][0];
p.y = s.y + dir[i][1];
p.dir = i;
if( s.dir==-1 )
p.turn = 0;
else
p.turn = s.dir==i ? s.turn : s.turn+1;
if( p.turn>2 || p.x<=0 || p.y<=0 || p.x>n || p.y>m || f[p.x][p.y] )
continue;
if( p==e ) {
return true;
}
if( map[p.x][p.y]!=0 )
continue;
if( dfs( p,e ) )
return true;
}
f[s.x][s.y] = 0;
return false;
}
int main() {
// freopen( "in.txt","r",stdin );
std::ios::sync_with_stdio( false );
int t;
Node s,e;
while( cin >> n >> m && n && m ) {
for( int i=1 ; i<=n ; i++ ) {
for( int j=1 ; j<=m ; j++ ) {
cin >> map[i][j];
}
}
cin >> t;
while( t-- ) {
cin >> s.x >> s.y >> e.x >> e.y;
if( map[s.x][s.y]!=map[e.x][e.y] || map[s.x][s.y]==0 || map[e.x][e.y]==0 )
goto PN;
s.turn = s.dir = -1;
memset( f,false,sizeof(f) );
if( dfs( s,e ) ) {
cout << "YES" << endl;
continue;
}
PN:
cout << "NO" << endl;
}
}
}