• 51Nod 1683 最短路


    题意

    给定一个未知的(0/1)矩阵,对每个(i)((1,1)sim(n,m))最短路为(i)的概率,在矩阵中不能向左走,路径长度为路径上权值为(1)的格子个数。

    (nleq6,mleq100。)

    思路

    打死都不可能想到状态设计DP系列

    参考了这篇博客的思路【51nod1683】最短路


    概率乘了(2^{n imes m})之后其实就是方案数,所以问题转化为了求满足题目条件的方案数

    发现(n)很小,最大只有(6),考虑状压,但是不能直接维护当前格子的最短路,因为在多条并列最短路时会重复计数

    考虑现在的(0/1)矩阵的特殊性:因为不能向左走,所以对于同一列中相邻两个格子之间的最短路最多相差(1)。因此考虑维护一整列最短路的差分数组。

    (zt)为一个三进制状态,表示该行从第二行开始,每个格子与上面的格子的差

    (f[i][j][zt])表示第(i)列,第一行的最短路为(j),第(2)行~第(n)行的最短路的三进制为(zt)的方案数

    转移时需要枚举下一列的(0/1)状态,线性更新一遍状态就可以了

    时间复杂度为(O(nm2^n3^{n-1}))

    代码

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define int long long
    using namespace std;
    
    const int A = 111;
    const int B = 1e6 + 11;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
        char c = getchar();
        int x = 0, f = 1;
        for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
        for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
        return x * f;
    }
    
    int zt[7], d[7], r[7], w[7], qwq, ans[A];
    int n, m, mod, f[A][A][A << 3], g[A << 3][1 << 6][2];
    
    signed main() {
        n = read(), m = read(), mod = read();
        qwq = (1 << n) - 1, zt[0] = 1;
        for (int i = 1; i <= n; i++) zt[i] = zt[i - 1] * 3;
        for (int s1 = 0; s1 <= zt[n - 1] - 1; s1++) {
            d[1] = 0;
            for (int i = 1; i <= n - 1; i++) d[i + 1] = d[i] + (s1 / zt[i - 1] % 3 - 1);
            for (int s2 = 0; s2 <= qwq; s2++) {
                for (int i = 1; i <= n; i++) w[i] = ((s2 & (1 << i - 1)) > 0), r[i] = d[i] + w[i];
                for (int i = 2; i <= n; i++) r[i] = min(r[i], r[i - 1] + w[i]);
                for (int i = n - 1; i >= 1; i--) r[i] = min(r[i], r[i + 1] + w[i]);
                g[s1][s2][0] = r[1];
                for (int i = 2; i <= n; i++) g[s1][s2][1] += (r[i] - r[i - 1] + 1) * zt[i - 2];
            }
        }
        memset(d, 0, sizeof(d));
        for (int s = 0; s <= qwq; s++) {
            for (int i = 1; i <= n; i++) d[i] = d[i - 1] + ((s & (1 << i - 1)) > 0);
            int t = 0;
            for (int i = 2; i <= n; i++) t += (d[i] - d[i - 1] + 1) * zt[i - 2];
            f[1][d[1]][t]++;
        }
        for (int i = 1; i <= m - 1; i++)
            for (int j = 0; j <= i; j++)
                for (int s = 0; s <= zt[n - 1] - 1; s++) {
                    if (!f[i][j][s]) continue;
                    for (int x = 0; x <= (1 << n) - 1; x++)
                        (f[i + 1][g[s][x][0] + j][g[s][x][1]] += f[i][j][s]) %= mod;
                }
        for (int j = 0; j <= m; j++)
            for (int s = 0; s <= zt[n - 1] - 1; s++) {
                if (!f[m][j][s])
                    continue;
                d[1] = j;
                for (int i = 1; i <= n - 1; i++) d[i + 1] = d[i] + (s / zt[i - 1] % 3 - 1);
                if (d[n] < 0) continue;
                (ans[d[n]] += f[m][j][s]) %= mod;
            }
        for (int i = 0; i <= n + m - 1; i++) cout << ans[i] << '
    ';
        return 0;
    }
    
  • 相关阅读:
    移动端触摸右侧菜单栏,页面内容对应项滚动到最上方
    swiper使用中一些点的总结
    javaScript正则表达式入门
    javaScript之数组操作方法(一)
    初识vue
    焦点控制切换和轮播
    文本内容只显示两行,然后加...
    图片父容器高度不定的图片垂直居中
    css3图片垂直居中
    【C#】两个list根据某个元素比较差集
  • 原文地址:https://www.cnblogs.com/loceaner/p/13247062.html
Copyright © 2020-2023  润新知