题目:有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;
}