[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;
}
原文地址:https://www.cnblogs.com/qixingzhi/p/9493636.html