构造 ZOJ 3810 A Volcanic Island

A Volcanic Island

Time Limit: 2 Seconds       Memory Limit: 65536 KB       Special Judge

An underwater volcano has erupted massively in somewhere of the deep Atlantis Ocean. This large eruption led to the birth of a new volcanic island, which had a shape of square. Near the island, there are N countries. All of them have claimed the sovereignty over the island.

After a lot of multilateral negotiation and occasional armed conflicts, the N countries decided to divide the square volcanic island equally. They partitioned the island into N x N small equal-sized square chunks. Each country could get a connected region consists of exact N chunks.

Two chunks A and B are called "connected" if they share an edge, or there exists another chunk C connected with both A and B. A group of chunks are called "connected region" if any two of these chunks are connected.

Every country want a unique region. It means the N regions should be different with each other. Two regions are considered as the same if and only if one can transform into the other by an isometry (a combination of rigid motions, including translation, rotation and reflection).

In a nutshell, your task is to divide a square island with N x N chunks into N connected regions with different shape. You also need to draw a map to color the regions of the map so that no two edge-adjacent regions have the same color. Most of the people in these countries believed that four different colors are enough. So you can mark these regions with at most four colors, red, green, blue and yellow.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

There is only an integer N (1 <= N <= 100).

Output

For each test case, output a valid map described above. If there is no solution, output "No solution!" instead. Please note that only four colors ('R', 'G', 'B' and 'Y') can be used to drawing the map.

Sample Input
2
2
5
Sample Output
No solution!
YYYGR
YGGGR
YGYYR
BYYYR
BBBBR

Author:  ZHOU, Yuchen
Source:  The 2014 ACM-ICPC Asia Mudanjiang Regional First Round


题意:有一个N*N的方格图,每个格子要涂上四种颜色中的一种,如果相邻的颜色相同,他们是同一个区域,现在需要输出一个N*N的方格图,方格图中的所有区域的形状两两不相同,如果翻转和旋转后相同当做是相同的。


思路:一看就知道是构造,但是智商真的是捉急啊,想不到什么好的构造办法,构造题一般是要以一两种特定的方式进行逐步的求解吧。。。感觉是这样,一开始乱来,想着会不会沿着外围走就行了呢?可惜是不行的,后来想到了一个构造方法,就是不断摆出"Z"行,但是发现只能摆n/2个,于是又没想下去了,其实这里已经比较接近答案了。最后还是看了题解才知道....Orz 

一个可能的构造形式如下:

10 * 10的时候

BBBBBBBBBY
GGGGGGGGBY
BBBBBBBGGY
GGGGGGBBBY
BBBBBGGGGY
YYYYBBBBBY
BBBYGGGGGY
YYBYYYYYGY
YYBBBBBBGY
YYYYYYGGGY 


于是要特判一些n比较小的情况,就行了....

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<(int)(b);++i)
#define rrep(i,b,a) for(int i=(b);i>=(int)(a);--i)
#define eps 1e-9
#define clr(a,x) memset(a,x,sizeof(a))
#define LL long long
const int maxn = 200+5;
const int Move[2][4] = { {1,0,-1,0},{0,1,0,-1} };
char col[] = "YBGR";
char ans[maxn][maxn];
char color[maxn];
bool adj[maxn][maxn];
int n;

/*形式如下
BBBBBBBBBY
GGGGGGGGBY
BBBBBBBGGY
GGGGGGBBBY
BBBBBGGGGY
YYYYBBBBBY
BBBYGGGGGY
YYBYYYYYGY
YYBBBBBBGY
YYYYYYGGGY 
*/
bool inRange(int r,int c) { return 0<=r&&r<n&&0<=c&&c<n; }
bool vis[maxn][4];
int rst[maxn];
int S[maxn*maxn],c;
bool build_color(int rest)
{
    if (rest == 0) return true;
    int k = -1;
    for(int i = 1; i <= n; ++i)
    if(color[i]==0 && (k == -1 || rst[i] < rst[k])) k = i;
    if (rst[k] == 0) return false;
    rep(i,0,4) {
        if (vis[k][i]) continue;
        vis[k][i] = true;
        color[k] = col[i];
        int cc = c;
        rep(j,1,n+1)
        if (adj[k][j] && !vis[j][i]) {
            vis[j][i] = true;
            --rst[j];
            S[c++] = j << 2 | i;
        }
        if (build_color(rest-1)) return true;
        vis[k][i] = false;
        while (c > cc) {
            int x = S[--c];
            vis[x>>2][x&3] = false;
            ++rst[x>>2];
        }
    }
    color[k] = 0;
    return false;
}

void out()
{
    puts("");
    rep(i,0,n) {
        rep(j,0,n) printf("%c",ans[i][j]+'0');
        puts("");
    }
    puts("");
}

bool solve()
{
    if (n == 2 || n == 3 || n == 4) return false;
    else if(n == 1) { puts("Y"); return true; }
    else if(n == 6) {
        puts("YYYYYY");
        puts("BBBRRB");
        puts("BBBRRB");
        puts("RYRRYB");
        puts("RYYYYB");
        puts("RRRRBB");
        return true;
    } else if (n == 5) {
        puts("YYYGR");
        puts("YGGGR");
        puts("YGYYR");
        puts("BYYYR");
        puts("BBBBR");
        return true;
    }

    clr(ans,0);
    int cnt = 1;
    rep(i,0,n) rep(j,0,n) ans[i][j] = cnt;
    ++cnt;
    rep(i,0,n) ans[i][n-1] = cnt;
    ++cnt;
    rep(i,0,n/2) {
        rep(j,0,n-1-i) ans[i][j] = cnt;
        rep(j,n-2-i,n-1) ans[i+1][j] = cnt;
        ++cnt;
    }
    rep(i,n/2,n) {
        if (i+2>n-2) break;
        rep(j,0,n-1-i) ans[i][j] = cnt;
        ans[i+1][n-2-i] = cnt;
        rep(j,n-2-i,n-2) ans[i+2][j] = cnt;
        ++cnt;
    }
    int len = 0;
    rep(j,n-1-n/2,n-1) {
        ans[n/2+1][j] = cnt; ++len;
    }
    rep(i,n/2+2,n) ans[i][n-2] = cnt, ++len;
    rrep(j,n-3,0) {
        if(len == n) break;
        ans[n-1][j] = cnt;
        ++len;
    }
//    out();

    clr(adj,0);
    rep(i,0,n) rep(j,0,n) rep(k,0,4) {
        int rr = i + Move[0][k];
        int cc = j + Move[1][k];
        if(!inRange(rr,cc)) continue;
        int u = ans[i][j],v=ans[rr][cc];
        if (u == v) continue;
        adj[u][v] = adj[v][u] = true;
    }
    rep(i,1,n+1) rst[i] = 4;
    clr(vis,0); c = 0; clr(color,0);
    build_color(n);
    rep(i,0,n) rep(j,0,n) {
     //   x = max((int)ans[i][j],x);
        assert(ans[i][j]<maxn);
        ans[i][j] = color[ans[i][j]];
    }
    rep(i,0,n)
    printf("%s\n",ans[i]);
    return true;
}

int main()
{
    int T; cin >> T;
    while (T--) {
        scanf("%d",&n);
        if (!solve()) puts("No solution!");
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值