题目描述:
http://acm.hdu.edu.cn/showproblem.php?pid=5113
中文大意:
尝试给 N×M 大小的棋盘上色,颜色有 K 种选择(从 1 到 K 编号),要求相邻两个格子的颜色不同。
属于四色问题的变形,但与“四色问题”不同的是,当前颜色有 K 种,且每种颜色有数量限制。
思路:
从棋盘的位置 0 开始,进行颜色的填涂。
每个位置,都有 k 种选择。其中,用完的,或者已经被四周单元格填涂过的颜色,不做考虑。
在当前位置填涂完毕后,继续尝试填涂下一位置,如此往复,直至棋盘填涂完毕。
注意剪枝,不然会超时。
剪枝情况:某种颜色的剩余数量 > 剩余格子数量的二分之一。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int nums[26];//各颜色的数量
bool have_result;//是否有解
int maps[5][5];//记录棋盘颜色信息
int end_maps[5][5];//最终的解
int dir[4][2] = {{0,-1}, {0,1}, {-1,0}, {1,0}};
//检查该位置是否可以填涂颜色 color
//即四周没有填涂该颜色
bool check(int row, int col, int color){
for(int j=0;j<4;j++){
int nx = col + dir[j][0];
int ny = row + dir[j][1];
if(nx>=0 && nx<m && ny>=0 && ny<n){
if(maps[ny][nx] == color){
return false;
}
}
}
return true;
}
//index:棋盘位置
void dfs(int index){
//剪枝:
//1.已经有了合适的答案
if(have_result){
return;
}
//2.找到一组解
if(index == n*m){
have_result = true;
memcpy(end_maps, maps, sizeof(maps));
return;
}
//3.当某种颜色的剩余数量 > 剩余格子数量的二分之一时
//即肯定有两个相邻格子要填涂一样的颜色
for(int i=1;i<=k;i++){
if(nums[i] > (n*m-index+1)/2){
return;
}
}
//尝试在当前位置填涂这 k 种颜色
for(int i=1;i<=k;i++){
//当前颜色已经用完
if(nums[i] == 0){
continue;
}
int col = index % m;
int row = index / m;
//四周没有当前颜色
if(check(row, col, i)){
maps[row][col] = i;
nums[i]--;
dfs(index+1);
//maps 和 nums 为公共数据,需要及时恢复,避免回溯时已被修改
maps[row][col] = 0;
nums[i]++;
}
}
}
int main(){
int t;
scanf("%d", &t);
for(int x=1;x<=t;x++){
scanf("%d %d %d", &n, &m, &k);
//各颜色的数量
for(int i=1;i<=k;i++){
scanf("%d", &nums[i]);
}
have_result = false;
memset(maps, 0, sizeof(maps));
dfs(0);
printf("Case #%d:
", x);
if(!have_result){
printf("NO
");
}
else{
printf("YES
");
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(j != 0){
printf(" ");
}
printf("%d", end_maps[i][j]);
}
printf("
");
}
}
}
}