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;
}