挖水井 最短路+超级源点

题目描述

农夫约翰决定给他的N(1<=N<=300)个牧场浇水,这些牧场被自然的命名为1..N。他可以给一个牧场引入水通过在这个牧场挖一口井或者修一条管道使这个牧场和一个已经有水的牧场连接。

在牧场i挖一口井的花费是w_i(1<=w_i<=100000)。修建一条水管连接牧场i和牧场j的花费是p_ij(1<=p_ij<=100000;p_ij=p_ji;p_ii=0)。

请确定农夫约翰为了完成浇灌所有的牧场所需的最小的总花费。

输入格式:

  • 第1行:一个单独的整数n。
  • 第2..n+1行:第i+1行包含一个单独的整数w_i。
  • 第n+2..2n+1行:第n+1+i行包含n个用空可分开的整数;其中第j个数是p_ij。

输入样例:

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

输出格式:

  • 第1行:一个单独的整数,表示花费。

输出样例:

9

输出说明:

农夫约翰可以在第4个牧场修井,并且将每个牧场和第一个连接起来,这样,花费是3+2+2+2=9。

分析:

  由题意可以知道,至少要在一个牧场上边挖井,每个牧场至少和一个有井的牧场相连,于是又要判断了,是挖井呢还是连上管道?判断的过程是很麻烦的,于是我们可以假设一个点为水源,即超级源点

,源点到每个点的距离为在这个点挖井的费用,这样就有了n+1个点,由每个点都必须间接或直接与水源相连,跑最小生成树即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=400+20;
struct Edge{
    int to,val,from;
    bool operator < (const Edge &A)const {
        return val<A.val;
    }
}e[N*N];
int w,len;
void Ins(int a,int b,int c){
    e[++len].to=b;e[len].val=c;e[len].from=a;
}
int n,f[N];
int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
void Mer(int a,int b){
    f[find(a)]=find(b);
}
int Krs(){
    for(int i=1;i<=n+1;i++)f[i]=i;
    int ans=0,cnt=0;
    sort(e+1,e+len+1);
    for(int i=1;i<=len;i++){
        int u=e[i].from,v=e[i].to;
        if(find(u)!=find(v)){
            ans+=e[i].val;
            Mer(u,v);
            if(++cnt==n)break;
        }
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&w);
        Ins(i,n+1,w);Ins(n+1,i,w);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&w);
            if(w)Ins(i,j,w);//这里看起来是加了有向边,但实际还是无向的,因为图是用邻接矩阵的形式给出
        }
    }
    printf("%d\n",Krs());
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/anyixing-fly/p/12421763.html