开心消消乐 一道有趣的搜索题 Apare_xzc

开心消消乐 BFS+DFS

2020/10/11

一个朋友问我的,他们学校内部OJ的题,觉得这题还是有点儿意思


题目描述:

        给一个n*m(1<=n,m<=10)的格子,每个格子里有一个数字,不同的数字代表颜色不同的方块。每次可以交换相邻的两个方块。 如果有三个或三个以上方块连在一起,那么就可以直接消去这些连载一起的方块。 空白的地方用0表示。问至少交换多少次能使得所有方块都被消去?题目保证有解。
输出最少的交换次数,以及每次交换的两个个格子(x1,y1) (x2,y2)的坐标


Sample Input1

5 4
0 2 2 0
0 0 1 0
1 2 2 1
0 1 0 0 
0 2 2 0

Sample Output1

2
2 3 3 3
3 2 4 2

Sample Input2

5 4
3 1 3 0
2 3 2 2
3 2 1 0
3 4 1 4
1 3 0 4

Sample Output2

4
1 2 2 2
4 2 4 3
5 1 5 2
2 2 3 2

样例解释

原来:    交换(2,3)和(3,3)后    消去连在一起的3个2     交换(3,2)和(4,2)    消去连在一起的3个2和4个1
0 2 2 0          0 2 2 0             0 0 0 0             0 0 0 0             0 0 0 0
0 0 1 0          0 0 2 0             0 0 0 0             0 0 0 0             0 0 0 0 
1 2 2 1   =>     1 2 1 1     =>      1 2 1 1    =>       1 1 1 1    =>       0 0 0 0
0 1 0 0          0 1 0 0             0 1 0 0             0 2 0 0             0 0 0 0
0 2 2 0          0 2 2 0             0 2 2 0             0 2 2 0             0 0 0 0
所以最少交换次数为2, 第一次交换(2,3)和(3,3), 第二次交换(3,2)和(4,2) 


题目分析

  • 因为n,m<=10很小,所以直接BFS。 我们每次从每个位置找,看能否与右边的块或下边的块交换。交换后消去颜色一样的3个及以上的连通块,判断此状态是否已经搜到过。如果没有搜到,就入队,并标记该状态已经搜到过。知道搜出全0的状态。
  • 我们可以用map<vector<vector>,int> 来标记状态,用vector<vector>来记录当前状态。
  • 求连通块的时候,我们可以dfs,找出与当前颜色一样的相邻的所有连通块的坐标存起来,然后判断是否>=3个,若大于,就全部消去。

代码

#include <bits/stdc++.h>
using namespace std;
struct Node{
    
      //记录交换方案,表示(x1,y1)和(x2,y2)交换
	int x1,y1,x2,y2;
	Node(int _x1=0,int _y1=0,int _x2=0,int _y2=0) {
    
    
		x1 = _x1; y1 = _y1; x2 = _x2; y2 = _y2;
	}
	void print() {
    
    
		printf("%d %d %d %d\n",x1+1,y1+1,x2+1,y2+1); 
	} 
};
//方向数组
const int dx[] = {
    
    1,0,-1,0};
const int dy[] = {
    
    0,1,0,-1};
int n,m;
bool check(vector<vector<int> >& v) {
    
      //判断n*m的格子是否全为0(已经都被消去)
	for(auto x:v) 
		for(auto y:x)
			if(y) return false;
	return true; 
}
void show(vector<Node>& route) {
    
     //输出交换方案
	printf("%d\n",(int)route.size());
	for(auto x:route)
		x.print();
}
void dfs(const vector<vector<int> >& tmp,vector<vector<int> >& vis,int x,int y,vector<pair<int,int> >& record) {
    
    
	//dfs找与tmp中当前方格(x,y)颜色相同的所有连通块, 并记录到record中
	if(tmp[x][y]==0||vis[x][y]) //此处没有方格,或者已经被标记
		return;
	vis[x][y] = true; //标记已访问
	record.push_back(make_pair(x,y));
	for(int i=0;i<4;++i) {
    
    
		int xx = x+dx[i], yy = y+dy[i];
		if(xx<0||xx>=n||yy<0||yy>=m) continue;
		if(tmp[x][y]==tmp[xx][yy]) //如果没出界并且颜色相同,就往过搜
			dfs(tmp,vis,xx,yy,record);
	} 
}
void bfs(vector<vector<int> >v) {
    
    
	map<vector<vector<int>>,int> mp;  //记录方格状态是否搜到过
	queue<pair<vector<vector<int> > ,vector<Node> > > Q;
	vector<Node> route; 
	mp[v] = 1;
	Q.push(make_pair(v,route));
	while(!Q.empty()) {
    
    
		vector<vector<int> > v = Q.front().first;
		route = Q.front().second;
		Q.pop();
		for(int i=0;i<n;++i) {
    
    
			for(int j=0;j<m;++j) {
    
    
				if(i+1<n && v[i][j]!=v[i+1][j]) {
    
    
					vector<vector<int> > tmp = v;
					swap(tmp[i][j],tmp[i+1][j]);  //交换(i,j)和(i+1,j)
					vector<vector<int> > vis(n,vector<int>(m,0));
					vector<pair<int,int> > r;
					dfs(tmp,vis,i,j,r);
					if(r.size()>=3) {
    
     //消去>=3的同色连通块
						for(auto tt:r)
							tmp[tt.first][tt.second] = 0;
					}
					for(int i=0;i<n;++i) for(int j=0;j<m;++j) vis[i][j] = 0;
					r.clear();
					dfs(tmp,vis,i+1,j,r);
					if(r.size()>=3) {
    
    
						for(auto tt:r)
							tmp[tt.first][tt.second] = 0;
					}
					if(!mp.count(tmp)) {
    
    
						mp[tmp] = 1;
						route.push_back(Node(i,j,i+1,j));
						Q.push(make_pair(tmp,route));
						if(check(tmp)) {
    
    
							show(route);
							return;
						}
						route.pop_back();
					}
				} 
				if(j+1<m && v[i][j+1]!=v[i][j]) {
    
    
					vector<vector<int> > tmp = v;
					swap(tmp[i][j],tmp[i][j+1]);
					vector<vector<int> > vis(n,vector<int>(m,0));
					vector<pair<int,int> > r;
					dfs(tmp,vis,i,j,r);
					if(r.size()>=3) {
    
    
						for(auto tt:r)
							tmp[tt.first][tt.second] = 0;
					}
					for(int i=0;i<n;++i) for(int j=0;j<m;++j) vis[i][j] = 0;
					r.clear();
					dfs(tmp,vis,i,j+1,r);
					if(r.size()>=3) {
    
    
						for(auto tt:r)
							tmp[tt.first][tt.second] = 0;
					}
					if(!mp.count(tmp)) {
    
    
						mp[tmp] = 1;
						route.push_back(Node(i,j,i,j+1));
						Q.push(make_pair(tmp,route));
						if(check(tmp)) {
    
    
							show(route);
							return;
						}
						route.pop_back();
					}
				}
			} 
		}
	} 
} 
int main(void) {
    
    
	scanf("%d%d",&n,&m);
	vector<vector<int> > v(n,vector<int>(m));
	bool flag = false;
	for(int i=0;i<n;++i) {
    
    
		for(int j=0;j<m;++j) {
    
    	
			scanf("%d",&v[i][j]);
			if(v[i][j]) flag = true;
		}
	}
	if(!flag) {
    
     //一开始没有块 
		puts("0");
		return 0;
	}
	//先判断初始状态是否可以消去 
	vector<vector<int> > vis(n,vector<int>(m,0));
	for(int i=0;i<n;++i) {
    
    
		for(int j=0;j<m;++j) {
    
    
			if(!vis[i][j]) {
    
    
				vector<pair<int,int> > record;
				dfs(v,vis,i,j,record);
				if(record.size()>=3) {
    
    
					for(auto tt:record)
						v[tt.first][tt.second] = 0;
				}
			}		
		}
	} 
	if(check(v)) {
    
    
		puts("0");
		return 0;
	}
	
	bfs(v);
	
	return 0;
}
/*
5 4
0 2 2 0
0 0 1 0
1 2 2 1
0 1 0 0 
0 2 2 0

5 4
3 1 3 0
2 3 2 2
3 2 1 0
3 4 1 4
1 3 0 4
*/

2020/10/11
xzc


猜你喜欢

转载自blog.csdn.net/qq_40531479/article/details/109007407