题目描述:
In mathematics, the four color theorem, or the four color map theorem, states that, given any separation of a plane into contiguous regions, producing a figure called a map, no more than four colors are required to color the regions of the map so that no two adjacent regions have the same color.
― Wikipedia, the free encyclopedia
In this problem, you have to solve the 4-color problem. Hey, I’m just joking.
You are asked to solve a similar problem:
Color an N × M chessboard with K colors numbered from 1 to K such that no two adjacent cells have the same color (two cells are adjacent if they share an edge). The i-th color should be used in exactly c i cells.
Matt hopes you can tell him a possible coloring.
Input
The first line contains only one integer T (1 ≤ T ≤ 5000), which indicates the number of test cases.
For each test case, the first line contains three integers: N, M, K (0 < N, M ≤ 5, 0 < K ≤ N × M ).
The second line contains K integers c i (c i > 0), denoting the number of cells where the i-th color should be used.
It’s guaranteed that c 1 + c 2 + ・ ・ ・ + c K = N × M .
题解:
两种解法:
(1)比赛时候过的。格子一定要考虑二分染色。然后猜想cmax<=格子数的黑格子就可以填下.之后我们把c从大到小排个序,c1染黑格子,c2接着染,但是注意,可能c2比较大,染完黑的之后开始染白的并且白的和黑的相接触了,比如5*5 的,c1=12,c2=12,c3=1,那么我们这时候应该倒着用c2染白色的格子.之后c3再次正着染黑色,黑色不够了正着染白色,并且c3一定不会再让黑色和白色相接触.因为3*(m*n/2)>2*(m*n/2).但是有一个例外的,就是2*3的,222的情况.因为向下取整太坑爹了..这种……所以我们先选取n和m中较大的来当做n,之后输出的时候再反向输出.然后发现貌似没有什么反例了…
其实我们强制第一个正着染黑色,第二个倒着染白色,然后第三个接着黑色染,之后仍然接着黑色就可以搞定啦.
(2)dfs爆搜+剪枝.暴力枚举当前的这一个格子可以填的可能性+回溯.剪枝为:剩下的cmax比剩下的格子数/2多就直接判为不行.直观感觉,如果颜色很多,那么剪枝就没什么用,但是很容易就搞到一个正确的答案.如果颜色很少,每个的次数很多,那么剪枝就可以发挥作用.
重点:
(1)格子图想二分染色
(2)写的时候耐心证明,小心小的边界坑爹情况.
(3)可以考虑下爆搜剪枝
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int maxn = 200+10;
int a[maxn][maxn], n, m, k, b[maxn][maxn];
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int c_m;
struct color
{
int no, num;
};
color c[maxn];
void ranZheng(int &x, int &y, int no, int &num, int a[][maxn])
{
int xx, yy;
int sum = (x+y)%2;
for(int j = y; j<=m; j+=2)
{
xx = x;
yy = j;
a[x][j] = no;
num--;
if(num==0)
{
y = j;
return;
}
}
for(int i = x+1; i<=n; i++)
{
int start = 1;
if((i+start)%2!=sum)
start = 2;
for(int j = start; j<=m; j+=2)
{
xx = i;
yy = j;
a[i][j] = no;
num--;
if(num==0)
{
x = i;
y = j;
return;
}
}
}
x = xx;
y = yy;
}
void ranFan(int no, int num, int a[][maxn])
{
for(int i = n; i>=1; i--)
{
int start = m;
if((start+i)%2==0)
start = m-1;
for(int j = start; j>=1; j-=2)
{
a[i][j] = no;
num--;
if(num==0)
return;
}
}
}
int check()
{
for(int i =1; i<=n; i++)
for(int j = 1; j<=m; j++)
{
if(a[i][j]==0)
continue;
for(int k = 0; k<4; k++)
{
int newi = i+dx[k], newj = j+dy[k];
if(a[i][j]==a[newi][newj])
return 0;
}
}
return 1;
}
bool cmp(color a, color b)
{
return a.num > b.num;
}
void getNext(int &x, int &y)
{
y+=2;
if(y>m)
{
x++;
if(y%2==1)
y = 2;
else
y = 1;
}
if(x>n)
{
x = 1, y = 2;
}
}
int solve()
{
int cnt = 0;
sort(c+1, c+1+k, cmp);
int limit = (n*m)/2;
if((n*m)%2==1)
limit++;
if(c[1].num>limit)
return 0;
memset(a, 0, sizeof(a));
int nowx = 1, nowy = 1;
int bx, by;
for(int i =1; i<=k; i++)
{
int tmpnum = c[i].num;
bx = nowx;
by = nowy;
for(int j = 1; j<=n; j++)
for(int kk = 1; kk<=m; kk++)
b[j][kk] = a[j][kk];
ranZheng(nowx, nowy, c[i].no, c[i].num, a);
getNext(nowx, nowy);
if(c[i].num!=0)
{
ranZheng(nowx, nowy, c[i].no, c[i].num, a);
getNext(nowx, nowy);
}
if(check()==0)
{
cnt++;
if(cnt==2)//不会再发生这种事情23333
while(1);
nowx = bx;
nowy = by;
c[i].num = tmpnum;
for(int j = 1; j<=n; j++)
for(int kk = 1; kk<=m; kk++)
a[j][kk] = b[j][kk];
ranFan(c[i].no, c[i].num, a);
}
}
if(check()==0)//可以用来看数据...
{
return 0;
while(1);
}
return 1;
}
int main()
{
freopen("2Bin.txt", "r", stdin);
//freopen("3Bout.txt", "w", stdout);
int ncase;
scanf("%d", &ncase);
for(int _ = 1; _<=ncase; _++)
{
printf("Case #%d:\n", _);
scanf("%d%d%d", &n ,&m, &k);
for(int i = 1; i<=k; i++)
{
scanf("%d", &c[i].num);
c[i].no = i;
}
int flag = 0;
if(n<m)
{
flag = 1;
swap(n, m);
}
int ans = solve();
if(ans==1)
{
printf("YES\n");
if(flag==0)
{
for(int i =1; i<=n; i++)
{
for(int j =1; j<=m; j++)
{
printf("%d%c", a[i][j], (j==m?'\n':' '));
}
}
}
else
{
for(int i = 1; i<=m; i++)
{
for(int j = 1; j<=n; j++)
{
printf("%d%c", a[j][i], (j==n?'\n':' '));
}
}
}
}
else
{
printf("NO\n");
}
}
return 0;
}