zoukankan      html  css  js  c++  java
  • NOIP 2017 P3959 宝藏 (状态压缩DP板子)

    洛谷题目传送门!!

    题目的N这么小,当然是选择用状压DP啦!  等等,我好像不会状缩。。。。

    首先,我们当然是要写状态转移方程了!!

    那么,如果我们设  f[s]  状态s下,所要的最小花费,那么很显然有状态转移方程:

    (s为总集合)

    f[(1 << j) | s] =  f[s] + num[i] * dis[i][j]  (从 i 点走向 j点时)

    其中,num表示之前已经走过的点的数量,dis表示从i到j的边的距离(这不就是题目要求吗)

    很显然,这道题用DFS来做比较方便(跑图和回溯)

    等等。

    如果我们用不同的走法更新到同一个点,那么是否会影响到我们的更新呢? 显然是会的。

    就比如,我们   1 -› 2 -› 3 -› 4  和 1 -›  3  -› 5 -› 2 - › 4  ,两种走法下,num的不同显然会给我们的更新带来不同的影响。

    所以,上面的DP走法是有后效性的。

    于是,我们要更新DP方程:

    f[1 << j | s] = min( best + num[i] * dis[i][j]) , 其中 best 为上一层的最优解。

    注意,起点不确定,怎么办?   当然是枚举起点啦!

    #include <bits/stdc++.h>
    using namespace std;
    #define N 13
    #define isdigit(c) ((c)>='0'&&(c)<='9')
    #define min(a,b) ((a)<(b)?(a):(b))
    
    inline int read(){
        int x = 0, s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-')s = -1;
            c = getchar();
        }
        while(isdigit(c)){
            x = (x << 1) + (x << 3) + (c ^ '0');
            c = getchar();
        }
        return x * s;
    }
    
    int f[N][1 << N][N];
    int num[N], dis[N][N];
    int n, m, limit;
    int ans = (int)2e9;
    
    /*
    状态转移方程:  
        对于当前的这个点j,如果他处在第 deth 层, 从第i号点转移到 j 则有
        f[j][1 << j | s][deth] = best + num[i] * dis[i][j] 
        best 表示上一层最优解,dis表示i和j之间的距离 
    */
    
    void dfs(int s, int best, int deth){
        if(best >= ans) return ;   /*剪枝*/
        if(s == limit) {
            ans = min(ans, best);   /*已经走完所有点*/
            return ;
        }
        for(int i = 0;i < n; i++){
            if(!(1 << i & s)) continue;   /*这个点还未走过,不能转移*/
            for(int j = 0;j < n; j++){
                if(!(1 << j & s) && dis[i][j] < (int)2e9){
                    if(f[j][1 << j | s][deth + 1] > best + num[i] * dis[i][j]){
                        f[j][1 << j | s][deth + 1] = best + num[i] * dis[i][j];  /*进行转移*/
                        num[j] = num[i] + 1;  /*deth + 1, 因为j在i的下一层*/
                        dfs(1 << j | s, f[j][1 << j | s][deth + 1], deth + 1);
                    }    
                }
            }
        }
        return ;
    }
    
    int main(){
        memset(dis, 127, sizeof(dis));
        n = read(), m = read();
        limit = (1 << n) - 1;
        for(int i = 1;i <= m; i++){
            int x = read() - 1, y = read() - 1, w = read();  /*编号减一, 给状压用(2 ^ 0 才是 1)*/
            dis[x][y] = dis[y][x] = min(dis[x][y], w);
        }
        for(int i = 0;i < n; i++){
            memset(num, 0, sizeof(num));
            memset(f, 127, sizeof(f));
            num[i] = 1;
            dfs(1 << i, 0, 0);  /*1 << i  把第i位设为 1*/
        }
        printf("%d
    ", ans);
        return 0;
    }
  • 相关阅读:
    django--模型层(orm)-查询补充及cookie
    django-模型层(model)-多表相关操作(图书管理练习)
    Linux-正则表达式与三剑客
    网络配置命令优先级和元字符
    Linux-Nginx和NFS
    Linux-内存进程和软件安装
    乌龟棋
    倍增 Tarjan 求LCA
    切蛋糕
    HDU1505 City Game/玉蟾宫/全0子矩阵 悬线法
  • 原文地址:https://www.cnblogs.com/wondering-world/p/12987392.html
Copyright © 2011-2022 走看看