1 条题解

  • 0
    @ 2025-8-24 21:45:56

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar kkksc03
    洛谷吉祥物 DA✩ZE

    搬运于2025-08-24 21:45:56,当前版本为作者最后更新于2013-10-07 22:37:04,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目大意:将1~n*m分别放入n*m的格子矩阵里,其中的一些格子有特殊要求(周围的8个格子都要比它大),问方案数。

    解决:从小到大放数,则一个普通格子只有等它周围的特殊格子都放完数了才能放数。在不考虑本题中“.”的格子不能是蓄水池的条件的情况下,我们可以写出如下记忆化搜索的方程式f[k][s]=sum{f[k-1][s’]}。其中sum为求和,k为放到了第几个数,s为蓄水池的放数状态(二进制状压),s’表示由s少放一个蓄水池能达到的状态。最后f[n*m][s全放]即为解。本题n<=4,m<=7,最多只有8个蓄水池,故此解可行。

    本题的难点在于“.”的格子不能是蓄水池。我们可以用容斥原理来解决。我们设在原来的基础上又增加了p个蓄水池,若p为奇数,则ans应减去f[n*m][s全放],否则为加上。由于本题对于蓄水池的限制很足(不能相邻),所以这样的枚举次数不会太多,耗时也不大。

    状态压缩记忆化搜索也可以写成搜索(即标程做法),所有测试点均在100ms内解决。

    
    #include <iostream>
    using namespace std;
    
    const int maxp = 8;
    const int maxs = 1 << maxp;
    const int mod = 12345678;
    const int maxrow = 4;
    const int maxcol = 7;
    const int maxn = maxrow * maxcol + 2;
    
    int n, m, ans;
    int f[maxs][maxn], x[maxp], y[maxp], tp;
    bool use[maxrow][maxcol];
    char graph[maxrow][maxcol];
    
    void init() {
        scanf("%d%d\n", &n, &m);
        for (int i = 0; i < n; ++i)
            gets(graph[i]);
    }
    
    bool in_range(int x, int y) {
        return x >= 0 && y >= 0 && x < n && y < m;
    }
    
    int calc() {
        tp = 0;
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (graph[i][j] == 'X') x[tp] = i, y[tp++] = j;
        memset(f, 0, sizeof(f));
        f[0][0] = 1;
        for (int s = 0; s < 1 << tp; ++s) {
            memset(use, 1, sizeof(use));
            for (int i = 0; i < tp; ++i)
                if (!(s & (1 << i)))
                    for (int dx = -1; dx <= 1; ++dx)
                        for (int dy = -1; dy <= 1; ++dy)
                            if (in_range(x[i] + dx, y[i] + dy)) use[x[i] + dx][y[i] + dy] = 0;
            
            int cnt = 0;
            for (int i = 0; i < n; ++i)
                for (int j = 0; j < m; ++j)
                    if (use[i][j]) ++cnt;
    
            for (int i = 0; i <= cnt; ++i)
                if (f[s][i]) {
                    f[s][i + 1] = (f[s][i + 1] + f[s][i] * (cnt - i)) % mod;
                    for (int j = 0; j < tp; ++j)
                        if (!(s & (1 << j))) f[s | (1 << j)][i + 1] = (f[s | (1 << j)][i + 1] + f[s][i]) % mod;
                }
        }
        return f[(1 << tp) - 1][n * m];
    }
    
    void search(int x, int y, int k) {
        if (x >= n) ans = (ans + k * calc()) % mod;
        else if (y >= m) search(x + 1, 0, k);
        else {
            search(x, y + 1, k);
            bool ok = 1;
            for (int dx = -1; dx <= 1; ++dx)
                for (int dy = -1; dy <= 1; ++dy)
                    if (in_range(x + dx, y + dy) && graph[x + dx][y + dy] == 'X')
                        ok = 0;
            if (ok) {
                graph[x][y] = 'X';
                search(x, y + 1, -k);
                graph[x][y] = '.';
            }
        }
    }
    
    int solve() {
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (graph[i][j] == 'X')
                    for (int dx = -1; dx <= 1; ++dx)
                        for (int dy = -1; dy <= 1; ++dy)
                            if ((dx || dy) && in_range(i + dx, j + dy) && graph[i + dx][j + dy] == 'X')
                                return 0;
        ans = 0;
        search(0, 0, 1);
        return (ans + mod) % mod;
    }
    
    int main() {
        freopen("mon.in", "r", stdin);
        freopen("mon.out", "w", stdout);
    
        init();
        printf("%d\n", solve());
        
        return 0;
    }
    
    
    

    [/cdec]

    • 1

    信息

    ID
    2233
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者