HDU-4531 吉哥系列故事——乾坤大挪移 模拟

题意:给定一个九宫格,然后能够选择某行或者是某列滚动,每个小方格分为上下左右四个块,每个块可以涂上4种不同的颜色。问最少使用多少步能够使得所有相同颜色相互联通。

分析:由于九宫格的所有的状态只有9!(362880)种,于是想起了先对每个格子进行标号,然后根据逆序对的数量进行一个递增进制数的hash处理,这样bfs搜索就能够去重了,接着只需要判断每个状态是否满足联通即可。这里的判定选择了先统计了不同颜色格子的数量然后dfs搜索,应该并查集也可以搞。滚动使用指针滚动即可,对于不能动的格子该格子所在行和列都不能够滚动。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;

// 9! = 362880
const int LIM = 362880;
int fac[9]; // 递增进制数的权值 
int tot[4]; // 四种颜色的个数 
char vis[LIM]; // hash九宫格 
char op[6]; // 滚动的操作的限制 
char hav[50]; // 4*9个格子的标记 

struct Block {
    char color[6];
    int id;
}bk[3][3];

struct Node {
    Block *ptr[3][3];
    int key;
    int ti;
    Node() {}
    Node(Block (*x)[3]) {
        ti = 0;
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                ptr[i][j] = &x[i][j]; 
            }
        }
        getkey();
    }
    void show() {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                printf("%5d", ptr[i][j]->id);
            }
            puts("");
        }
        puts("");
    }
    void getkey() {
        int cnt;
        key = 0;
        for (int i = 0; i < 8; ++i) {
            cnt = 0;
            for (int j = i+1; j < 9; ++j) {
                if (ptr[j/3][j%3]->id < ptr[i/3][i%3]->id) ++cnt;
            }
            key += cnt * fac[8-i];
        }
    }
    int dfs(int x, int y, int z) {
        if (hav[(x*3+y)*4+z]) return 0;
        hav[(x*3+y)*4+z] = 1;
        char ch = ptr[x][y]->color[z];
        int ret = 1;
        if (z == 0) {
            if (ptr[x][y]->color[2] == ch) ret += dfs(x, y, 2);
            if (ptr[x][y]->color[3] == ch) ret += dfs(x, y, 3);
            if (x > 0 && ptr[x-1][y]->color[1] == ch) ret += dfs(x-1, y, 1);
        } else if (z == 1) {
            if (ptr[x][y]->color[2] == ch) ret += dfs(x, y, 2);
            if (ptr[x][y]->color[3] == ch) ret += dfs(x, y, 3);
            if (x < 2 && ptr[x+1][y]->color[0] == ch) ret += dfs(x+1, y, 0);
        } else if (z == 2) {
            if (ptr[x][y]->color[0] == ch) ret += dfs(x, y, 0);
            if (ptr[x][y]->color[1] == ch) ret += dfs(x, y, 1);
            if (y > 0 && ptr[x][y-1]->color[3] == ch) ret += dfs(x, y-1, 3);
        } else {
            if (ptr[x][y]->color[0] == ch) ret += dfs(x, y, 0);
            if (ptr[x][y]->color[1] == ch) ret += dfs(x, y, 1);
            if (y < 2 && ptr[x][y+1]->color[2] == ch) ret += dfs(x, y+1, 2);
        }
        return ret;
    }
    bool judge() {
        memset(hav, 0, sizeof (hav));
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                for (int k = 0; k < 4; ++k) {
                    if (!hav[(i*3+j)*4+k]) {
                        int cnt = dfs(i, j, k);
                        switch(ptr[i][j]->color[k]) {
                            case 'R': if (cnt != tot[0]) return false; break;
                            case 'G': if (cnt != tot[1]) return false; break;
                            case 'B': if (cnt != tot[2]) return false; break;
                            default : if (cnt != tot[3]) return false;
                        }
                    }
                }
            }
        }
        return true;
    }
};

void pre() {
    fac[1] = 1;
    for (int i = 2; i < 9; ++i) {
        fac[i] = fac[i-1] * i;
    }
}

void swapr(Block *x[], Block *y[], int dir) { // 横向的滚动
    if (dir == 0) { // 向左
        y[0] = x[1], y[1] = x[2], y[2] = x[0];
    } else { // 向右
        y[0] = x[2], y[1] = x[0], y[2] = x[1];
    }
}

void swapc(Block *x[], Block *y[], int dir) { // 纵向的滚动
    if (dir == 0) { // 向上 
        y[0] = x[3], y[3] = x[6], y[6] = x[0];
    } else { // 向下 
        y[0] = x[6], y[3] = x[0], y[6] = x[3];
    }
}

int bfs() {
    queue<Node>q;
    Node tmp = Node(bk);
    Node pos;
    memset(vis, 0, sizeof (vis));
    q.push(tmp); // 初始化是没有逆序对的
    vis[tmp.key] = 1;
    while (!q.empty()) {
        pos = q.front();
        q.pop();
        if (pos.judge()) {
            return pos.ti;
        }
        for (int i = 0; i < 6; ++i) {
            if (op[i]) continue;
            tmp = pos;
            if (i < 3) { // 横向的滚动 
                for (int j = 0; j < 2; ++j) {
                    swapr(&pos.ptr[i][0], &tmp.ptr[i][0], j);
                    tmp.getkey();
                    tmp.ti = pos.ti + 1;
                    if (!vis[tmp.key]) {
                        vis[tmp.key] = 1;
                        q.push(tmp);
                    }
                }
            } else { // 纵向的 
                for (int j = 0; j < 2; ++j) {
                    swapc(&pos.ptr[0][i%3], &tmp.ptr[0][i%3], j);
                    tmp.getkey();
                    tmp.ti = pos.ti + 1;
                    if (!vis[tmp.key]) {
                        vis[tmp.key] = 1;
                        q.push(tmp);
                    }
                }
            }
        }
    }
}

int main() {
    int T, ca = 0;
    pre();
    scanf("%d", &T);
    while (T--) {
        memset(op, 0, sizeof (op));
        memset(tot, 0, sizeof (tot));
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                scanf("%s", bk[i][j].color);
                for (int k = 0; k < 4; ++k) {
                    switch(bk[i][j].color[k]) {
                        case 'R': ++tot[0]; break;
                        case 'G': ++tot[1]; break;
                        case 'B': ++tot[2]; break;
                        default : ++tot[3];
                    }
                }
                bk[i][j].id = i*3+j; // 0-8
                if (bk[i][j].color[4] == '1') {
                    op[i] = op[3+j] = 1; // 两种操作无法进行
                }
            }
        }
        printf("Case #%d: %d
", ++ca, bfs());
    }
    return 0;
}