• [NOIp2017] 宝藏


    类型:状压

    传送门:>Here<

    题意:给出$N$个点藏有宝藏,有$M$条可以打通的边(都未打通)。你可以选任意一个点作为起点出发,每一次可以打通一条边来挖目的地的宝藏(不能打通已经挖完宝藏的两个点之间的边),其中打通一条边的费用是$L imes K$,其中$L$表示边的长度,$K$表示从当前点到起点打通的路径经过了几个点。问挖完所有宝藏的最小总费用

    解题思路

    由于$N leq 12$,考虑状压。其中二进制状态$S$中为$1$的那一位表示已经挖掘

    可以枚举起点$i$,设立数组$dp[S]$表示以$i$为起点且到达状态$S$所需要的最小费用,因此答案就是$Min{ dp[2^n-1] }$

    然后从起点开始搜索,每一次枚举已经挖掘的所有点(设当前点为$j$,当前状态为$S$),再枚举这一次要打通的点$k$。记录$dis[j]$表示从$j$到起点经过的点的数量,也就是题目中的$K$。则$$dp[S|k] =  Min{dp[S] + dis[j]*cost(j ightarrow k)}$$

    这样的复杂度并不是$N!$,因为$dp$数组是有条件更新的(有点类似记搜)

    Code

    注意需要判断$i$到$j$有边

    /*By DennyQi 2018.8.17*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    #define  Max(a,b)  (((a)>(b)) ? (a) : (b))
    #define  Min(a,b)  (((a)<(b)) ? (a) : (b))
    using namespace std;
    typedef long long ll;
    const int MAXN = 10010;
    const int MAXM = 27010;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register int c = getchar();
        while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
        if(c == '-') w = -1, c = getchar();
        while(c >= '0' && c <= '9') x = (x<<3) + (x<<1) + c - '0', c = getchar();return x * w;
    }
    int N,M,x,y,z,ans;
    int G[15][15],dp[8195],dis[15];
    void DFS(int sta){
        for(int i = 1; i <= N; ++i){
            if(!(sta & (1<<(i-1)))) continue;
            for(int j = 1; j <= N; ++j){
                if(sta & (1<<(j-1))) continue;
                if(G[i][j] == INF) continue;
                if(dis[i] * G[i][j] + dp[sta] < dp[sta | (1<<(j-1))]){
                    dp[sta | (1<<(j-1))] = dis[i] * G[i][j] + dp[sta];
                    dis[j] = dis[i] + 1;
                    DFS(sta | (1<<(j-1)));
                }
            }
        }
    }
    int main(){
        N=r,M=r;
        memset(G, 0x3f, sizeof G);
        ans = INF;
        for(int i = 1; i <= M; ++i){
            x=r,y=r,z=r;
            if(z < G[x][y]){
                G[x][y] = z;
                G[y][x] = z;
            }
        }
        for(int i = 1; i <= N; ++i){
            memset(dp, 0x3f, sizeof dp);
            memset(dis, 0, sizeof dis);
            dis[i] = 1;
            dp[1<<(i-1)] = 0;
            DFS(1<<(i-1));
            ans = Min(ans, dp[(1<<N) - 1]);
        }
        printf("%d", ans);
        return 0;
    }
  • 相关阅读:
    抬起头,看到满天星星
    别再嫌弃你妈妈话多唠叨啦,她可能正在做声音健脑操呢
    此生不能不認識的一個人
    长尾夹除了夹东西还能做什么?
    Markdown使用经验总结
    注册quora失败
    电脑插入耳机后声音仍然外放
    centOS下安装tree命令
    解决ubuntu “无法获得锁"
    虚拟机上Ubuntu无法上网问题
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9493636.html
Copyright © 2020-2023  润新知