洛谷 P1550 [USACO08OCT]打井Watering Hole

题目背景

John的农场缺水了!!!
题目描述

农民John 决定将水引入到他的n(1<=n<=300)个牧场。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第i 号田中挖一口井需要花费W_i(1<=W_i<=100,000)元。连接i 号田与j 号田需要P_ij (1 <= P_ij <= 100,000 , P_ji=P_ij)元。

请求出农民John 需要为连通整个牧场的每一块田地所需要的钱数。
输入输出格式
输入格式:

第1 行为一个整数n。

第2 到n+1 行每行一个整数,从上到下分别为W_1 到W_n。

第n+2 到2n+1 行为一个矩阵,表示需要的经费(P_ij)。

输出格式:

只有一行,为一个整数,表示所需要的钱数。

输入输出样例
输入样例#1:

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

输出样例#1:

9

思路
最小生成树
需要注意的是不一定建一棵树,因为我们可以多棵树,挖多个井;
处理方法:设置一个超级原点,将各个井与超级原点连边,边权为打井所需花费,然后构建最小生成树。
证明:将打井费用作为边权,这样在做最小生成树时,就能直接选择打井(与超级原点相连)还是连边(与已打井相连),输出答案为所有边权。

#include
#include
#include
using namespace std;
const int maxn=310*310;
struct node{
    int x;int y;int v;
}tu[maxn];


int n,ans,num,tot,a;
int fa[400],cost[maxn];
bool cmp(node c,node d){
    return c.vint father(int x){
    if(fa[x]==x) return x;
    return fa[x]=father(fa[x]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&cost[i]);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            scanf("%d",&a);{
                if(i!=j){
                    tu[++tot].v=a;
                    tu[tot].x=i;tu[tot].y=j;
                }
                else {
                    tu[++tot].v=cost[i];//与超级原点连边
                    tu[tot].x=0;
                    tu[tot].y=i;
                }
            }
        }
    for(int i=0;i<=n;i++)
    fa[i]=i;
    sort(tu+1,tu+tot+1,cmp);
    for(int i=1;i<=tot;i++){
        int xx=father(tu[i].x);int yy=father(tu[i].y);
        if(xx!=yy){
            ans+=tu[i].v;
            fa[xx]=yy;
            num++;
        }
        if(num==n) break;
    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(===图论===,最小生成树)