hdu 3311 Dig The Wells 斯坦纳树模板

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3311

 

题意: 

       给你n个寺庙,m个村庄,p条路,现在你要在这n+m个位置中选出若干个位置打井,每个位置打井的费用会告诉你,同时p条路也有修建费用,现在每个寺庙都住着一个和尚,问你最小的费用让这n个和尚都能喝上水。

 

一些过程写进了代码,主要用的是状压的思想。具体的解释还是看博客比较好。。

     


#include<bits/stdc++.h>
#define mp(a,b) make_pair(a,b)
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=1010;
const int inf=0x3fffffff;;
vector<pii> ve[maxn];
int dis[maxn][maxn],dp[(1<<7)][maxn];
int n,m,p,wei[maxn];
void spfa(){
    int inq[maxn]={0},k;
    queue<int> Q;
    for(int s=0;s<=n+m;s++){
        for(int j=0;j<=n+m;j++)
        dis[s][j]=inf;
        dis[s][s]=0;
        //两两间先置距离为inf
        Q.push(s);
        while(!Q.empty()){  //对每个点都为起点做一次spfa
            int st=Q.front(); Q.pop();
            inq[st]=0;
            int num=ve[st].size();
            for(int j=0;j<num;j++){
                int v=ve[st][j].first;//point
                if(dis[s][v]>dis[s][st]+ve[st][j].second){
                    dis[s][v]=dis[s][st]+ve[st][j].second;
                    if(!inq[v])
                    Q.push(v),inq[v]=1;
                }
            }
        }
    }
}
void Dp(){
    //所有的状态先变成inf
    for(int state=1;state<(1<<(n+1));state++){
        for(int i=0;i<=n+m;i++){
            dp[state][i]=inf;
        }
    }
    //两两点之间的dp值先进行赋值
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n+m;j++){
            dp[(1<<i)][j]=dis[i][j];
        }
    }

    for(int state=1;state<(1<<(n+1));state++){
        if(state&(state-1)){
            for(int i=0;i<=n+m;i++){ //以每个点为子树开始遍历子集
                for(int sub=state;sub>0;sub=(sub-1)&state)
                    dp[state][i]=min(dp[state][i],dp[state^sub][i]+dp[sub][i]);
            }
            for(int i=0;i<=n+m;i++){//通过边来进行更新
                for(int j=0;j<=n+m;j++){
                    dp[state][i]=min(dp[state][i],dp[state][j]+dis[j][i]);
                }
                //printf("dp[state][i] = %d   i = %d\n",dp[state][i],i);
            }
        }
    }
}
int main(){
    while(~scanf("%d%d%d",&n,&m,&p)){

        for(int i=0;i<=m+n;i++) ve[i].clear();
        for(int i=1;i<=n+m;i++) {
            int v;
            scanf("%d",&v);
            ve[0].pb(mp(i,v));
            ve[i].pb(mp(0,v));
        }
        //将点0置为井,因最后会为一棵树故所有n点与0点都会连接
        //即为点0-n必选的权值最小的生成树
        for(int i=1;i<=p;i++){
            int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
            ve[x].pb(mp(y,c));
            ve[y].pb(mp(x,c));
        }
        spfa();
        /*for(int i=0;i<=n+m;i++){
            for(int j=0;j<=n+m;j++){
                printf("i=%d j=%d dis i j =%d\n",i,j,dis[i][j]);
            }
        }*/
        Dp();
        int now=(1<<(1+n))-1;
        printf("%d\n",dp[(now)][0]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值