1601: [Usaco2008 Oct]灌水
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 2103 Solved: 1384
[ Submit][ Status][ Discuss]
Description
Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。 建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0). 计算Farmer John所需的最少代价。
Input
*第一行:一个数n
*第二行到第n+1行:第i+1行含有一个数wi
*第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。
Output
*第一行:一个单独的数代表最小代价.
Sample Input
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Sample Output
输出详解:
Farmer John在第四块土地上建立水库,然后把其他的都连向那一个,这样就要花费3+2+2+2=9
HINT
Source
HOME Back
一道十分有趣的题目呢~
这道题我们肯定可以贪心的想到最后构成的肯定是 [树 / 森林];因为若是构成了有环图的话,则肯定可以删去一条边使其依然连通 ,所以有环的无向图一定不优,那边只能是无环无向图了(其实就是树);
那么这道题就可以去往最小生成树的方面去想了,但本题有一个特殊的在于由于可以在某些田地里建水塔,则有可能建出来的是一片森林而不是树(森林中的每一个树都有一个水塔点);那么最简单的最小生成树便不一定是答案了不是么?
那咋办?根据树的性质和最小生成树的特点便会有一种谈心的解法:
对于并查集的集合来说,我们当其为一个独立联通并有水灌溉的树,记录下其最小代价和在该最小代价下的水塔代价(可以简单的贪心证明每个块只需一个水塔即可)对于在最小生成树时,我们对于判断是否选择该边除了看边联通的两点是否在同一集合内还要多附加一个条件:该边的代价是否比联通的两个集合中那个较大的水塔代价小,若是则合并并算出新的集合的代价:集合A的代价 + 集合B的代价 + 联通边的代价 - max(集合A水塔代价 , 集合B水塔代价);合并的集合的水塔代价为min(集合A水塔代价 , 集合B水塔代价);
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 110001
using namespace std;
long long n , lon_;
long long p[M] , w[M] , st[M];
long long s[M] , e[M] , lon[M] , ran[M] , cnt;
bool cmp(const long long &a , const long long &b){return lon[a] < lon[b];}
long long find(long long x){return x == p[x] ? x : p[x] = find(p[x]); }
int main(){
scanf("%lld",&n);
for(long long i = 1 ; i <= n ; ++i){
scanf("%lld",&w[i]);
st[i] = w[i];
p[i] = i;
}
for(long long i = 1 ; i <= n ; ++i)
for(long long j = 1 ; j <= n ; ++j){
scanf("%lld",&lon_);
if(i < j){
cnt++;
s[cnt] = i , e[cnt] = j , lon[cnt] = lon_ , ran[cnt] = cnt;
}
}
sort(ran + 1 , ran + 1 + cnt , cmp);
for(long long i = 1 ; i <= cnt ; ++i){
long long v = ran[i];
long long x = find(s[v]) , y = find(e[v]);
if(x != y && lon[v] < max(st[x] , st[y])){
if(st[x] > st[y])swap(x , y);
p[y] = x;
w[x] = w[x] + w[y] - st[y] + lon[v];
}
}
long long ans = 0;
for(long long i = 1 ; i <= n ; ++i)
if(p[i] == i)
ans += w[i];
printf("%lld",ans);
}