题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1175
题目:“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
题解:
最开始没注意到的坑点:
bfs+优先队列,最开始没用优先队列,wa了好几发,后来看了讨论区里一位朋友的测试数据,发现开始点和结束点倒过来竟然结果不一样。
突然明白虽然你这样能求出最短的路程,但是有可能多转弯了几次,结果就变NO了,所以要用优先队列让转弯次数少的先出队列。
其他的要点和解释在代码注释中
代码:
import java.util.PriorityQueue;
import java.util.Scanner;
//bfs+优先队列,最开始没用优先队列,wa了好几发,后来看了讨论区里一位朋友的测试数据,发现开始点和结束点倒过来竟然结果不一样。
//突然明白虽然你这样能求出最短的路程,但是有可能多转弯了几次,结果就变NO了,所以要用优先队列让转弯次数少的先出队列。
public class Main {
static int n,m,q;
static boolean isin(int a,int b){//判断是否在地图内
return a>0&&a<=n&&b>0&&b<=m;
}
static int res[][]={{1,0},{-1,0},{0,1},{0,-1}};//上下左右移动
static class point implements Comparable<point>{
int x,y,step;//坐标点,当前折叠次数
char direction;//上一次的折叠方向
public point(int x, int y, int step, char direction) {
this.x = x;
this.y = y;
this.step = step;
this.direction = direction;
}
@Override
public int compareTo(point o) {//step小的先出队列
return this.step-o.step;
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(true){
n=sc.nextInt();
m=sc.nextInt();
if(n==0&&m==0)
break;
int map[][]=new int[n+1][m+1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
map[i][j]=sc.nextInt();
}
}
q=sc.nextInt();
int x1,y1,x2,y2;
for(int i=0;i<q;i++){
x1=sc.nextInt();
y1=sc.nextInt();
x2=sc.nextInt();
y2=sc.nextInt();
if(map[x1][y1]!=map[x2][y2]||map[x1][y1]==0){//如果起点和终点不一样或者有一个是0就不能消除
System.out.println("NO");
continue;
}
if(x1==x2&&y1==y2){//起点和终点相等也不能消除
System.out.println("NO");
continue;
}
boolean vis[][]=new boolean[n+1][m+1]; //判断是否访问过
PriorityQueue<point> q=new PriorityQueue<>();
q.add(new point(x1,y1,-1,'a'));//起点入队列,因为起点没有上一步走的方向,所以我用‘a’代替,这样相当于多转了一个弯
vis[x1][y1]=true; //所以step初始化为-1,为0也可以,判断条件变成3就好
boolean flag=false;//标记是否能消除成功
while(!q.isEmpty()){
point tp=q.poll();
int x=tp.x;
int y=tp.y;
int step=tp.step;
char direction=tp.direction;
if(step>2){//如果折叠超过2次就消除失败
break;
}
if(x==x2&&y==y2){
flag=true;
break;
}
for(int j=0;j<4;j++){
int tx=x+res[j][0];
int ty=y+res[j][1];
if((isin(tx,ty)&&!vis[tx][ty]&&map[tx][ty]==0)||(tx==x2&&ty==y2)){//如果下一个点是终点或者是0且未被访问过的且在地图内的
if(j==0){//自定义四个方向
char t_direction='D';
if(t_direction==direction){
q.add(new point(tx,ty,step,t_direction));
vis[tx][ty]=true;
}else{
q.add(new point(tx,ty,step+1,t_direction));//跟上一次折叠的方向不一样就step+1
vis[tx][ty]=true;
}
}else if(j==1){
char t_direction='U';
if(t_direction==direction){
q.add(new point(tx,ty,step,t_direction));
vis[tx][ty]=true;
}else{
q.add(new point(tx,ty,step+1,t_direction));
vis[tx][ty]=true;
}
}else if(j==2){
char t_direction='L';
if(t_direction==direction){
q.add(new point(tx,ty,step,t_direction));
vis[tx][ty]=true;
}else{
q.add(new point(tx,ty,step+1,t_direction));
vis[tx][ty]=true;
}
}else{
char t_direction='R';
if(t_direction==direction){
q.add(new point(tx,ty,step,t_direction));
vis[tx][ty]=true;
}else{
q.add(new point(tx,ty,step+1,t_direction));
vis[tx][ty]=true;
}
}
}
}
}
if(flag){
System.out.println("YES");
}else{
System.err.println("NO");
}
}
}
sc.close();
}
}