题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5113
看了看有的博客,看了hdu的discuss都是这样剪枝的…
即代码中的注释:当剩余的cell的一半(向上取整)小于某个颜色的剩余数,必有两个相邻cell颜色相同。
很好理解,因为颜色一定会用完,剩余的cell一半以上(不包括)会有同样的颜色。只看一种颜色的情况,在颜色不相邻的情况下,使得染色数目最多的方式就是两两染色的cell之间有一个空cell,如果为奇数,则染色数目为(n+1)/2,若为偶数则为n/2,这时如果该颜色还有剩余则必将填入间隔的空cell中,则必有相邻cell颜色相同。
大概这就是为什么说学算法要数学好吧,数学是最需要奇思妙想的科目了…
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int dir[4][2] = { {-1,0},{0,-1}, {1,0},{0,1} };
int color[30], a[10][10];
int T, N, M, K, kase = 0, ALL;
bool ok;
bool check(int row, int col) {
if (row < 0 || row >= N || col < 0 || col >= M)return false;
return true;
}
bool judge(int row, int col, int x) {
for (int e = 0; e < 4; e++) {
int row2 = row + dir[e][0];
int col2 = col + dir[e][1];
if (check(row2, col2)) {
if (a[row2][col2] == x)return false;
}
}
return true;
}
inline void dfs(int pos) {
for (int i = 1; i <= K; i++) //当剩余的cell的一半(向上取整)小于某个颜色的剩余数,必有两个相邻cell颜色相同
if ((ALL + 1 - pos) / 2 < color[i]) return;
if (pos == ALL) {
ok = true; return;
}
int row = pos / M;
int col = pos % M;
for (int i = 1; i <= K; i++) {
if (!color[i])continue;
if (judge(row,col,i)) {
a[row][col] = i;
color[i]--;
dfs(pos + 1);
if (!ok) {
a[row][col] = 0;
color[i]++;
}
else return;
}
}
}
int main(void) {
scanf("%d", &T);
while (T--) {
scanf("%d %d %d", &N, &M, &K);
for (int i = 1; i <= K; i++)
scanf("%d", &color[i]);
ALL = N * M;
ok = false;
memset(a, 0, sizeof(a));
printf("Case #%d:\n", ++kase);
dfs(0);
if (!ok)printf("NO\n");
else {
printf("YES\n");
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)
printf("%d%c", a[i][j], j == M - 1 ? '\n' : ' ');
}
}
return 0;
}