Acwing 529.宝藏 (最小生成树+状压DP)

题目

参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了n个深埋在地下的宝藏屋,也给出了这n个宝藏屋之间可供开发的m条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。

小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。

已经开凿出的道路可以任意通行不消耗代价。

每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。

另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。

新开发一条道路的代价是:

这条道路的长度 × 从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋)。

请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。

输入格式
第一行两个用空格分离的正整数 n 和 m,代表宝藏屋的个数和道路数。

接下来 m 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏屋的编号(编号为1~n),和这条道路的长度v。

输出格式
输出共一行,一个正整数,表示最小的总代价。

数据范围
1≤n≤12,
0≤m≤1000,
v≤5∗105
输入样例:
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1

思路

n小于等于12,很容易想到状压dp。题意的话就让你找一个最小生成树,是的这个生成树的在题目的定义下权值最小。由题意可知,题目定义的权值除了距离外只和树高有关,假设我们已经有了一棵树,但是其所包含的点并不完全,并且剩下的点与其连通,并且这些点组成了树的第j-1层,那么我们要求的就是所有第j层的点到前面j-1层内部点的最短距离。所以我们的思路应该是预处理每一个状态的合法子集。然后对一个状态的合法子集去求出消费的权值,最后dp一下就好了。

代码实现

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005 
#define fi first 
#define se second
#define pb push_back
typedef long long ll;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd (ll a,ll b) {return b?gcd (b,a%b):a; }
inline int read() {
    char ch=getchar(); int x=0, f=1;
    while(ch<'0'||ch>'9') {
        if(ch=='-') f = -1;
        ch=getchar();
    } 
    while('0'<=ch&&ch<='9') {
        x=x*10+ch-'0';
        ch=getchar();
    }   return x*f;
}

const int maxn=12;
const int M=1<<maxn;
int g[maxn][maxn];
int dp[M][maxn];
int pre[maxn];
int n,m;

int  main () {
    freopen ("data.in","r",stdin);
    cin>>n>>m;
    MT (g,0x3f);
    rev (i,0,n) g[i][i]=0;

    while (m--) {
        int a,b,c;
        cin>>a>>b>>c;
        a--,b--;
        g[a][b]=g[b][a]=min (g[a][b],c);
    }
    rev (i,1,1<<n) 
       rev (j,0,n)  if (i>>j&1) {
           rev (k,0,n) if (g[j][k]!=inf) pre[i]|=1<<k;
       }
    MT (dp,0x3f);
    rev (i,0,n) dp[1<<i][0]=0;
    
    rev (i,1,1<<n) 
     for (int j=(i-1);j;j=(j-1)&i) 
        if ((pre[j]&i)==i) {
            int remain=i^j;
            int cost=0;
            rev (k,0,n) if (remain>>k&1) {
                int t=inf;
                rev (u,0,n) if (j>>u&1) t=min (t,g[k][u]);
                cost+=t;
            }
            rev (k,1,n) dp[i][k]=min (dp[i][k],dp[j][k-1]+cost*k);
        }
    int ans=inf;
    rev (i,0,n) ans=min (ans,dp[(1<<n)-1][i]);
    cout<<ans<<endl; 
    fclose (stdin);
    return 0;
}
posted @ 2020-08-09 21:46  Luglucky  阅读(122)  评论(0编辑  收藏  举报