CCF201412-4 最优灌溉 就是生成树嘛~

时间:2023-01-20 21:35:18

问题描述   雷雷承包了很多片麦田,为了灌溉这些麦田,雷雷在第一个麦田挖了一口很深的水井,所有的麦田都从这口井来引水灌溉。
  为了灌溉,雷雷需要建立一些水渠,以连接水井和麦田,雷雷也可以利用部分麦田作为“中转站”,利用水渠连接不同的麦田,这样只要一片麦田能被灌溉,则与其连接的麦田也能被灌溉。
  现在雷雷知道哪些麦田之间可以建设水渠和建设每个水渠所需要的费用(注意不是所有麦田之间都可以建立水渠)。请问灌溉所有麦田最少需要多少费用来修建水渠。

第一眼看成了最短路,写了半天才想起来,明明一个麦田可以连接多个麦田,这不是最小生成树么...

于是就选了比较简单的Prim算法。

关于这个算法其实原理特简单:

1、建立一个点的集合(已经经过的点)

2、更新其余点到这个集合最近的距离

3、选择点加入这个集合

其中更新距离的问题就是目标点到集合中所有店的距离里的最小距离作为其到该集合的最小距离。

把起点放到点集里,然后不断选择距离最近的点加进去就好了。顺便记录一下花销。

#include<iostream>
#include<string.h>
using namespace std;

#define MAXN 1000+5
#define INF 0x3f3f3f3f

int n,m,sum;
int map[MAXN][MAXN]; //存图
int dis[MAXN]; // 其他点与点集的最小距离
int flag[MAXN]; // 标记点集

int solve(){
flag[1] = 1; // 标记起点
for (int i = 1; i <= n; i++){// 初始化起点到各边距离
dis[i] = map[1][i];
map[i][i] = 0;
}
int min, mk;
for (int i = 1; i<n; i++){
min = INF; // 寻找最小值
for (int j = 1; j <= n; j++){
if (!flag[j] && dis[j] < min){
min = dis[j]; mk = j;
}
}
sum += min; // 记录消费
flag[mk]++; // 标记访问
for (int i = 1; i <= n; i++){ // 更新距离
if (!flag[i] && dis[i] > map[mk][i])
dis[i] = map[mk][i];
}
}
}

int main(){
cin >> n >> m;
memset(map, INF, sizeof(map));
for (int i = 0; i < m; i++){
int a, b, c;// 无向图输入
cin >> a >> b >> c;
map[a][b] = c; map[b][a] = c;
}
solve();
cout << sum << endl;
return 0;
}