HDU - 5004 KAMI(回溯+dfs)

本文介绍了一款基于KAMI游戏规则的染色问题解决方法,通过暴力搜索和深度优先遍历实现,针对16*10的网格,探讨如何在限定步数内将不同颜色统一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:
这题的规则和KAMI游戏的规则相同。可以百度一下这个游戏。
规则大概如下:
有一个 16*10 由4种颜色组成的图,你可以用任意一种颜色对这个图上颜色不相同的点进行染色,染色的同时,其周围上的颜色相同的点,也被染色成你要染的颜色。
问你能否用至少n步,将图片染成1中颜色。

解析:
此题给出了最小步数n<=8,显然是限制了dfs的深度,因此就是暴力搜索题。
其实对于每次选择一个点变颜色,等价于每次都对同一个点改变颜色。
因此就可以枚举一个点,然后bfs做出与该点所在的联通块相邻的联通块都有哪些颜色,基于本体联通块的定义,这些颜色必定不同于该点的颜色,然后枚举要把该点变成那些颜色,然后bfs染色,继续搜索就可以了。
最终当图被染成一种颜色,则结束搜索。

总结:这题比较难,记得当时网络赛,好像没人能做出来,对于每次选择一个点变颜色,等价于每次都对同一个点改变颜色。

这个方法实在是太难想了,但是我又很想把这题给做出来,因为觉得KAMI这个游戏很有趣,于是上网搜索了大神的题解,照着他的思路敲了一遍。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int ROW = 16;
const int COL = 10;
const int dr[] = {-1, 0, 1, 0};
const int dc[] = { 0, 1, 0,-1};
struct Node {
	int r,c;
	Node() {}
	Node(int _r,int _c) {
		r = _r;
		c = _c;
	}
};
int n; //最小步数
bool ok;
char grid[ROW+1][COL+1];
int ans[ROW*COL];
bool full() {
	for(int i = 1; i <= ROW; i++) {
		for(int j = 1; j <= COL; j++) {
			if(grid[i][j] != grid[1][1]) {
				return false;
			}
		}
	}
	return true;
}
void dfs(int x,int y,int cur) {
	char now[ROW+1][COL+1];
	int vis[ROW+1][COL+1];

	if(cur == n) {
		if(full()) {
			ok = true;
		}
		return ;
	}
	int color = grid[x][y] - '0';
	for(int i = 1; i <= 4; i++) { //枚举颜色
		if(color == i) { //如果要染的颜色和当前的颜色相同,则不要枚举
			continue;
		}
		memset(vis,0,sizeof(vis));
		memcpy(now,grid,sizeof(grid));
		//将连通区域进行染色
		queue<Node> que;
		que.push(Node(x,y));
		vis[x][y] = true;
		int r,c;
		while(!que.empty()) {
			Node front = que.front();
			que.pop();
			grid[front.r][front.c] = i + '0';
			for(int d = 0; d < 4; d++) {
				r = front.r + dr[d];
				c = front.c + dc[d];
				if(grid[r][c] - '0' != color || vis[r][c]) {
					continue;
				}
				vis[r][c] = true;
				que.push(Node(r,c));
			}
		}
		ans[cur] = i;
		dfs(x,y,cur+1);
		if(ok) {
			return ;
		}
		memcpy(grid,now,sizeof(grid));
	}
}
void solve() {
	scanf("%d",&n);
	memset(grid,0,sizeof(grid));
	for(int i = 1; i <= ROW; i++) {
		scanf("%s",grid[i]+1);
	}
	for(int i = 1; i <= ROW; i++) {
		for(int j = 1; j <= COL; j++) {
			if(grid[i-1][j] == grid[i][j] || grid[i][j-1] == grid[i][j]) {
				//如果当前点前面连通的点无法覆盖成功,则后面的点就不需要考虑
				continue;
			}
			ok = false;
			dfs(i,j,0);
			if(ok) {
				for(int k = 0; k < n; k++) 
					printf("%d %d %d\n",ans[k],i,j);
				return;
			}
		}
	}
}
int main() {
	int t,cas = 1;
	scanf("%d",&t);
	while(t--) {
		printf("Case #%d:\n",cas++);
		solve();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值