DFS深搜的剪枝。
题目大意,输入一个t(1 <= t < =5000)有t组测试数据,每一组测试数据,第一行输入n,m,k,(0 <=n , m <= 5 , 0 <= k <= 25)第二行 然后k个数,在n * m的矩阵中用着k中颜色填充每个格子,dii中颜色有有k[i]个,共n * m个颜色(注意是k种颜色,共有n * m个颜色),去填充着n*m个格子,每个格子的上向左右填充的颜色不能相同,要求找到一组这样的解,若存在一种解,则输出YES,然后输出每个单元填充的颜色,否则输出NO。若存在多种可行解,输出任意一种即可。
首先你肯定会想到dfs,去尝试用剩下的颜色填充某个格子,若将所有格子都填充完了,并且每个格子的上下左右都不相同,则这是可行解,否则就没有解。但是这一定会超时,你得有优秀的剪枝。最重要的剪枝:当前状态还剩下cnt个格子没有填,某种颜色剩下x个,若某个x大于格子数的一半,即x > (cnt + 1) / 2,则此次尝试一定是不行的。具体请读者画图尝试一下。
下面贴出代码,供大家参考。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n, m, k, arr[26], maze[6][6], color[26];
int dir[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};
//填x,y这个点,maze里还有cnt个格子未填
bool dfs(int x, int y, int cnt)
{
//如果填完了,就true
if(!cnt) return true;
//如果x,y超出范围就返回false
if(x <= 0 || x > n || y <= 0 || y > m) return false;
//剪枝:如果剩余的格子数的一半大于某颜色,则这种做法就不行,返回false
for(int i = 1; i <= k; ++i)
{
if((cnt + 1) / 2 < arr[i])
return false;
}
bool vised[26];
memset(vised, false, sizeof vised);
//vised记录当前x,y点的上下左右填的颜色,x,y不能填
for(int i = 0; i < 4; ++i)
{
int dx = x + dir[i][0], dy = y + dir[i][1];
if(dx > 0 && dx <= n && dy > 0 && dy <= m)
vised[maze[dx][dy]] = true;
}
for(int i = 1; i <= k; ++i)//尝试i颜色
{
if(arr[i] > 0 && !vised[i])
{
arr[i]--;
maze[x][y] = i;
for(int j = 0; j < 4; ++j)
{
int dx = x + dir[j][0], dy = y + dir[j][1];
if(maze[dx][dy] == 0 && dfs(dx, dy, cnt - 1))
return true;
}
arr[i]++;
maze[x][y] = 0;
}
}
return false;
}
int main()
{
int t;
scanf("%d", &t);
for(int I = 1; I <= t; ++I)
{
memset(maze, 0, sizeof maze);
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k; ++i)
{
scanf("%d", &arr[i]);
color[i] = arr[i];
}
printf("Case #%d:\n", I);
if(dfs(1, 1, m * n))
{
puts("YES");
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
printf("%d%c", maze[i][j], j < m? ' ': '\n');
}
}
else
puts("NO");
}
return 0;
}