[NOIP 2011] Mayan游戏:搜索,模拟

题意:寻找一个用n步(0<n<=5)完成5*7的Mayan游戏的字典序最小的方案。

每个方块可以左移、右移,至多5步,共有少于5.6亿个可能的情形。3秒内搜一搜是可行的。

怎样消除方块呢?由于要求一次性消完所有能消的方块,而且横、竖有公共部分则全部消掉,所以不能找到就消。找到连续三个就标记,每次循环根据标记进行清除,循环到不能再消。

怎样使方块下落呢?从下往上模拟,让方块掉到不能再掉的地方,而不是只掉一格。

BZOJ上NOIP十连测里做了一道模拟,虽然游戏不同,但是对下落的模拟是类似的。

朴素的搜索(暴力)TLE两组。什么地方可以剪枝呢?最优性?可行性?似乎都不行。有没有做重复的工作呢?有。如果有相邻的两个方块,把左边的往右移和把右边的往左移是等价的,由于要求字典序最小,考虑前者即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int X = 5, Y = 7, N = 5, sz = X*Y*sizeof(int);
int n, M[X][Y];

struct op {
    int x, y, g;
} S[N];

inline bool empty()
{
    for (int i = 0; i < X; ++i)
        for (int j = 0; j < Y; ++j)
            if (M[i][j])
                return false;
    return true;
}

void fall()
{
    for (int i = 0; i < X; ++i)
        for (int j = 1; j < Y; ++j) {
            if (!M[i][j])
                continue;
            for (int k = j-1; k >= 0 && !M[i][k]; --k)
                swap(M[i][k], M[i][k+1]);
        }
}

void clear()
{
    static bool b[X][Y];
    memset(b, 0, sizeof(b));
    bool ok = false;
    do {
        fall();
        ok = true;
        for (int i = 1; i < X-1; ++i)
            for (int j = 0; j < Y; ++j)
                if (M[i][j] && M[i-1][j] == M[i][j] && M[i][j] == M[i+1][j])
                    b[i-1][j] = b[i][j] = b[i+1][j] = true;
        for (int i = 0; i < X; ++i)
            for (int j = 1; j < Y-1; ++j)
                if (M[i][j] && M[i][j-1] == M[i][j] && M[i][j] == M[i][j+1])
                    b[i][j-1] = b[i][j] = b[i][j+1] = true;
        for (int i = 0; i < X; ++i)
            for (int j = 0; j < Y; ++j)
                if (b[i][j]) {
                    ok = false;
                    b[i][j] = false;
                    M[i][j] = 0;
                }
    } while (!ok);
}

bool dfs(int);

inline bool move(int d, int i, int j, int g, int T[X][Y])
{
    S[d] = (op){i, j, g};
    swap(M[i][j], M[i+g][j]);
    clear();
    bool ret = dfs(d+1);
    memcpy(M, T, sz);
    return ret;
}

bool dfs(int d)
{
    if (d == n)
        return empty();
    int T[X][Y];
    memcpy(T, M, sz);
    for (int i = 0; i < X; ++i)
        for (int j = 0; j < Y; ++j) {
            if (!M[i][j])
                continue;
            if (i < X-1 && move(d, i, j, 1, T)
                || i && !M[i-1][j] && move(d, i, j, -1, T))
                return true;
        }
    return false;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < X; ++i)
        for (int j = 0, c; scanf("%d", &c), c; ++j)
            M[i][j] = c;

    if (!dfs(0))
        puts("-1");
    else
        for (int i = 0; i < n; ++i)
            printf("%d %d %d\n", S[i].x, S[i].y, S[i].g);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值