灌水(USACO)

灌水

时间限制 : 1000 MS   空间限制 : 65536 KB


问题描述


Farmer John决定给他分别用1到N(1 <= N <= 300)分别编号的牧草浇水,他可以直接在一颗牧草旁边直接挖一口井来获得水,也可以用管子从任意有水的牧草那里来获得水。 
在第i颗牧草旁边挖一口井的代价为Wi(1 <= W_i <= 100,000),用管子连接第i与第j颗牧草的代价为Pij(1 <= Pij <=100,000; Pij = Pji; Pii=0) 
请求出Farmer John浇灌这些牧草花费的最小代价。

输入格式


第一行,一个整数N 
第二行到第N+1行,行i+1表示Wi 
第N+2行到第2N+1行,行N+1+i包含N个用空格分隔开来的整数,每行第j个数字即是Pij

输出格式
仅一行,Farmer John浇灌这些牧草的最小代价。

样例输入


4





0 2 2 2 

2 0 3 3 

2 3 0 4 

2 3 4 0

样例输出

9

解题:

1.**<最小生成树>**

2.**虚点**

难点:在哪儿打井????

**正经的解法(放大招)**

算法框架:

1.设立一个虚拟源点,表示水源

2.虚拟源点与每颗牧草连边,边权为在对应牧草旁挖井的费用

3.跑最小生成树即可。
 

/*
问题:
1.分不清点与边(全部)
2.tot与 i计算重复 
*/
#include 

using namespace std;

struct node{
    int a, b, len;
};

int w[300 + 5] = { };
node edge[90000 + 5] = { };
int father[90000 + 5] = { };
int n = 0;
int tot = 0;

bool cmp(node a, node b)
{
    return a.len < b.len;    
}

int getfather(int x)
{
    if(x != father[x]){
        father[x] = getfather(father[x]);    
    }
    return father[x];
}

void  kruskal()
{
    int x, y, k, cnt, tt;
    cnt = 0;
    k = 0;
    tt= 0;
    while(cnt < n){
        k++;
        x = getfather(edge[k].a);
        y = getfather(edge[k].b);
        if(x != y){
            father[x] = y;
            tt += edge[k].len;
            cnt++;
        }
    }
    printf("%d", tt);    
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &w[i]);
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            int t = 0;
            scanf("%d", &t);
            if(i != j){
                tot++;
                edge[tot].a = i;
                edge[tot].b = j;
                edge[tot].len = t;
            }
        }
    }
    for(int i = 1; i <= n; i++){
        edge[tot + i].a = n + 1;
        edge[tot + i].b = i;
        edge[tot + i].len = w[i];
    }
    tot += n;//2 
    for(int i = 1; i <= n + 1; i++){
        father[i] = i;
    }
    sort(edge + 1, edge + tot + 1, cmp);
    kruskal();
    
    return 0;
}
```


 

你可能感兴趣的:(信奥)