2014北京 HDU 5113 Black And White


题目描述:

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值