HDU5113--Black And White(dfs加剪枝)

题目:有N*M个格子的棋盘,用K种颜色去涂,相邻格子不能同色。给定每种颜色要涂的格子数,如果能满足题意,则输出YES和任意一种涂法,不能输出NO。
思路:从左上到右下,深搜加剪枝,不剪的话会超时。
剪枝方法:目前剩余的格子数number,因为相邻不能涂一样颜色,所以当(number+1)/2 <(某种颜色要涂的格子数),直接剪掉,找出最大值或者遍历都可以

#include <iostream>
using namespace std;
#define N 10
#define K 50
int n, m, k;
int color[K];
int flag;//标记是否找到
int a[N][N];//存储结果
int dir[2][2] = { 0, -1, -1, 0 };
bool check(int x, int y, int icolor)
{
	int i;
	for (i = 0; i < 2; i++)
	{
		//i==0表示左边的一格   i==1表示上面的一格
		int xx = x + dir[i][0], yy = y + dir[i][1];
		//若超出边界,说明他就在边界
		if (xx >= 0 && xx < n && yy >= 0 && yy < m && a[xx][yy] == icolor)
			return false;
	}
	return true;
}
void dfs(int x, int y, int number)//当前格子(未涂色)的坐标以及剩余的未涂色的格子数
{
	//找到了直接退出
	if (flag)
		return;
	//当剩余为0的时候表明已经找到了,
	if (number == 0)
	{
		flag = 1;
		return;
	}
	//遍历一下,只要有一种颜色不够涂就剪枝,效果更明显
	for (int i = 1; i <= k; i++)
	{
		if ((number + 1) / 2 < color[i])//这种颜色已经放不下了-------------------剪枝
			return;
	}
	
	//扩展方式,k种颜色,从1开始,因为颜色编号就是从1开始的
	for (int i = 1; i <= k; i++)
	{
		if (color[i] == 0)//这种颜色已经够了,不用再涂了-------------------------约束性剪枝
			continue;
		if (!check(x, y, i))//这种颜色不适合放-----------------------------------约束性剪枝
			continue;
		
		//可以放这种颜色
		a[x][y] = i;
		color[i]--;
		if (y == m - 1)//当前格是在最后一列时
			dfs(x + 1, 0, number - 1);
		else
			dfs(x, y + 1, number - 1);
		if (flag)//找到了,立即返回
			return;
		color[i]++;//回溯
	}
}
int main()
{
	int t;
	cin >> t;
	int z = 1;//案例编号
	while (t--)
	{
		cin >> n >> m >> k;
		//输入每种颜色的数量,从1开始
		for (int i = 1; i <= k; i++)
			cin >> color[i];
		flag = 0;
		dfs(0, 0, n * m);//进入0 0这一个,剩余n*m个空位置
		cout << "Case #" << z << ':' << endl;
		if (flag)
		{
			cout << "YES" << endl;
			//输出结果
			for (int i = 0; i < n; i++)
			{
				for (int j = 0; j < m-1; j++)
					cout << a[i][j] << ' ';
				cout << a[i][m - 1] << endl;
			}
				
		}
		else
			cout << "NO" << endl;
		z++;
	}
	return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值