zoukankan      html  css  js  c++  java
  • loj6274 数字 题解

    PPT

    loj6274 数字 题解

    ——DP套DP的神级操作

    岸芷汀兰


    首先考虑朴素做法。我们枚举 (V),检查是否能存在 (x)(y) 满足 (xland y=V)

    设DP状态 (f[i, 0/1,0/1,0/1,0/1]) 表示当前考虑了前 (i) 位,是否存在一组 (x)(y),满足 (xland y=V)(xlor y = T)(Lxleq xleq Rx)(Lyleq yleq Ry)。后面那四组 (0/1) 表示 (x)(y) 是否贴着上下界。

    相信各位同学也都能看出来,这就是一个简单的数位DP!

    从高位向低位转移即可。


    代码片段:

    inline bool solve() {
        mem(f, 0);
        f[11][1][1][1][1] = 1;
        for (int i = 11; i >= 2; -- i) { 
            for (int lx = 0; lx <= 1; ++ lx) {// 第i位
                for (int rx = 0; rx <= 1; ++ rx) {
                    for (int ly = 0; ly <= 1; ++ ly) {
                        for (int ry = 0; ry <= 1; ++ ry) {
                            for (int x = 0; x <= 1; ++ x) { // 第i-1位
                                for (int y = 0; y <= 1; ++ y) {
                                    if (!f[i][lx][rx][ly][ry]) continue;
    
                                    if (lx == 1 && Lx[i - 1] == 1 && x == 0) continue;
                                    if (rx == 1 && Rx[i - 1] == 0 && x == 1) continue;
                                    if (ly == 1 && Ly[i - 1] == 1 && y == 0) continue;
                                    if (ry == 1 && Ry[i - 1] == 0 && y == 1) continue;
    
                                    if ((x | y) != T[i - 1]) continue;
                                    if ((x & y) != V[i - 1]) continue;
    
                                    f[i - 1][lx & (x == Lx[i - 1])][rx & (x == Rx[i - 1])][ly & (y == Ly[i - 1])][ry & (y == Ry[i - 1])] = 1;
                                }
                            }
                        }
                    }
                }
            }
        }
        for (int lx = 0; lx <= 1; ++ lx) {
            for (int rx = 0; rx <= 1; ++ rx) {
                for (int ly = 0; ly <= 1; ++ ly) {
                    for (int ry = 0; ry <= 1; ++ ry) {
                        if (f[1][lx][rx][ly][ry]) return true;
                    }
                }
            }
        }
        return false;
    }
    

    现在考虑如何优化这个算法。

    我们发现每次枚举 (V) 的过程太耗时。于是这启发我们思考能不能直接计算出合法 (V) 的数量。

    对于一个确定的 (V) 来说,(f) 数组肯定是确定的。但如果反过来,一套确定的 (f) 数组可不一定对应着唯一的 (V)。所以,如果我们能对于某一套 (f) 数组,直接算出有多少个 (V) 会算出这一套 (f) 并累加到答案中,计算效率就会提高不少。


    现在就要祭出大法:DP套DP!

    设外层DP状态 (F[i,S]) 表示当前考虑了前 (i) 位,(f[i]) 数组的“情况”是 (S),所对应的 (V) 的数量。

    其中 (S) 是一个16位二进制数。第一位代表 (f[i,0,0,0,0]) 的值,第二位代表 (f[i,0,0,0,1]) 的值,第三位代表 (f[i,0,0,1,0]) 的值……以此类推。

    怎么转移呢?

    首先枚举位数 (i)。其次枚举 (S)。再其次枚举 (V) 的第 (i) 位是0还是1。然后用我们刚才提出的小 (f) 的转移方法,用 (S) 能转移出 (P) 来。最后把 (F[i,S]) 累加到 (f[i-1,P]) 中即可。

    具体实现见代码。这份代码不开O3过不去,原因是我每次直接把 (f) 数组求出来了,如果直接使用位运算会快很多。


    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    #define FILEIN(s) freopen(s".in", "r", stdin);
    #define FILEOUT(s) freopen(s".out", "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline long long read(void) {
        long long x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int maxn = 63;
    
    bool f[2][2][2][2], g[2][2][2][2];
    char T[maxn], Lx[maxn], Rx[maxn], Ly[maxn], Ry[maxn];
    long long F[maxn][70000];
    
    inline void change(long long x, char str[]) {
        int tot = 0;
        while (x) {
            str[++ tot] = x % 2;
            x /= 2;
        }
    }
    
    inline int call(int s, int i) { return (s >> i) & 1; }
    
    inline int get(int i, int st, int v) {
        mem(g, 0);
        int cnt = 0;
        for (int lx = 0; lx <= 1; ++ lx) { // 第i位
            for (int rx = 0; rx <= 1; ++ rx) {
                for (int ly = 0; ly <= 1; ++ ly) {
                    for (int ry = 0; ry <= 1; ++ ry) {
                        f[lx][rx][ly][ry] = call(st, cnt ++);
                        if (!f[lx][rx][ly][ry]) continue;
    
                        for (int x = 0; x <= 1; ++ x) { // 第i-1位
                            for (int y = 0; y <= 1; ++ y) {
                                if (lx == 1 && Lx[i - 1] == 1 && x == 0) continue;
                                if (rx == 1 && Rx[i - 1] == 0 && x == 1) continue;
    

                                if (ly == 1 && Ly[i - 1] == 1 && y == 0) continue;
                                if (ry == 1 && Ry[i - 1] == 0 && y == 1) continue;
    
                                if ((x | y) != T[i - 1]) continue;
                                if ((x & y) != v) continue;
    
                                g[lx & (x == Lx[i - 1])][rx & (x == Rx[i - 1])][ly & (y == Ly[i - 1])][ry & (y == Ry[i - 1])] = 1;
                            }
                        }
                    }
                }
            }
        }
        cnt = 0;
        int res = 0;
        for (int lx = 0; lx <= 1; ++ lx) {
            for (int rx = 0; rx <= 1; ++ rx) {
                for (int ly = 0; ly <= 1; ++ ly) {
                    for (int ry = 0; ry <= 1; ++ ry) {
                        res |= (g[lx][rx][ly][ry] << cnt);
                        ++ cnt;
                    }
                }
            }
        }
        return res;
    }
    
    int main() {
        change(read(), T); 
        change(read(), Lx); change(read(), Rx);
        change(read(), Ly); change(read(), Ry);
        F[61][1 << 15] = 1;
        for (int i = 61; i >= 2; -- i) {
            for (int st = 0; st < (1 << 16); ++ st) {
                for (int v = 0; v <= 1; ++ v) { // V[i - 1]
                    int to = get(i, st, v);
                    F[i - 1][to] += F[i][st];
                }
            }
        }
        long long ans = 0;
        for (int s = 1; s < (1 << 16); ++ s) ans += F[1][s];
        printf("%lld
    ", ans);
        return 0;
    }
  • 相关阅读:
    8.CNN应用于手写字识别
    8.优化器
    6.正则化
    5.Dropout
    4.交叉熵
    3.Minst数据集分类
    2.非线性回归
    1.线性回归
    110. Balanced Binary Tree
    106. Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14802316.html
Copyright © 2011-2022 走看看