zoukankan      html  css  js  c++  java
  • Luogu 3959 [NOIP2017] 宝藏

    NOIP2017最后一道题

    挺难想的状压dp。

    受到深度的条件限制,所以一般的状态设计带有后效性,这时候考虑把深度作为一维,这样子可以保证所有状态不重复计算一遍。

    神仙预处理:先处理出一个点连到一个集合所需要的最小代价,然后再处理出一个集合连到一个集合所需要的最小代价

    设$g_{s, t}$表示从s集合连到t集合的最小代价, $f_{i, j}$表示当前深度为i,挖到集合s的最小代价,有转移:

        $f_{i, s} = min(g_{s, t} * (i - 1) + f_{i - 1, t})$  t是s的子集

    最后的答案  $ans = min(f_{i, maxS})$ $(0<i<n)$

    可以发现这样子最优答案一定会被计算到。

    时间复杂度$O(3^{n} * 2 ^ {n} * n)$.

    Code:

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 15;
    const int S = (1 << 12) + 5;
    const int inf = 0x3f3f3f3f;
    
    int n, m, e[N][N], h[N][S], g[S][S], f[N][S];
    
    inline void read(int &X) {
        X = 0;
        char ch = 0;
        int op = 1;
        for(; ch > '9' || ch < '0'; ch = getchar())
            if(ch == '-') op = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    inline int min(int x, int y) {
        return x > y ? y : x;
    }
    
    inline void chkMin(int &x, int y) {
        if(y < x) x = y;
    }
    
    int main() {
        read(n), read(m);
        memset(e, 0x3f, sizeof(e));
        for(int x, y, v, i = 1; i <= m; i++) {
            read(x), read(y), read(v);
            e[x][y] = min(e[x][y], v);
            e[y][x] = min(e[y][x], v);
        }
        
    /*    for(int i = 1; i <= n; i++, printf("
    "))
            for(int j = 1; j <= n; j++)
                printf("%d ", e[i][j]);   */
        
        for(int i = 1; i <= n; i++) {
            for(int s = 1; s < (1 << n); s++) {
                h[i][s] = inf;
                if(!(s & (1 << (i - 1))))
                    for(int j = 1; j <= n; j++)
                        if(s & (1 << (j - 1)))
                            chkMin(h[i][s], e[i][j]);
            }
        }
        
        for(int s = 1; s < (1 << n); s++) {
            for(int t = s & (s - 1); t; t = s & (t - 1)) {
                int x = s ^ t;
                for(int i = 1; i <= n; i++) 
                    if(x & (1 << (i - 1)))
                        g[s][t] = min(g[s][t] + h[i][t], inf);
            }
        }
        
        memset(f, 0x3f, sizeof(f));
        for(int i = 1; i <= n; i++)    f[1][1 << (i - 1)] = 0;
        for(int i = 2; i <= n; i++) {
            for(int s = 1; s < (1 << n); s++) {
                for(int t = s & (s - 1); t; t = s & (t - 1)) {
                    int tmp;
                    if(g[s][t] != inf) tmp = g[s][t] * (i - 1);
                    else tmp = inf;
                    if(f[i - 1][t] != inf) 
                        chkMin(f[i][s], f[i - 1][t] + tmp);
                }
            }
        }
        
        int ans = inf;
        for(int i = 1; i <= n; i++)
            chkMin(ans, f[i][(1 << n) - 1]);
        
        printf("%d
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    poj2411 状压dp
    棋盘覆盖TYVJ1035(二分图最大匹配)
    poj3417
    无向图边双+缩点
    无向图点双+缩点
    bzoj1123(割点加路径统计)
    【BZOJ1178】会议中心(APIO2009)-贪心+倍增+set
    【BZOJ4650】优秀的拆分(NOI2016)-后缀数组+RMQ+差分
    【BZOJ4569】萌萌哒(SCOI2016)-并查集+倍增
    【BZOJ2208】连通数(JSOI2010)-SCC+DP+bitset
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9479388.html
Copyright © 2011-2022 走看看