10.5 最小生成树
10.5 最小生成树
http://codeup.hustoj.com/contest.php?cid=100000622
A 还是畅通工程

题目解析
没啥可说的,常规题。
Prime算法适合稠密图,邻接矩阵O(V2);邻接表O(V2+E),采取堆优化【用priority_queue】能降低到O(VlogV+E)
Kruskal算法适合稀疏图,O(ElogE),其中logE用于对边的排序
(V为定点数,E为边数)
🙈感觉Kruskal更好的样子,之后最小生成树的题也都用Kruskal做了
⚠️ 使用Prime时要注意默认点是0还是1,有的题目点从1开始计的,如果代码还是默认0就会错误
(当然你默认1、2、3也可以😂反正要在n以内)
代码(Prime)
#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 105
#define INF 0x3fffffff
using namespace std;
struct node {
int v, dis;
node(int _v, int _dis) : v(_v), dis(_dis) {}
};
int n;
vector<node> Adj[maxn];
bool vis[maxn];
int d[maxn];//各点到集合S的距离
int prime() {
fill(d, d + maxn, INF);
d[1] = 0; //默认从顶点1开始
int ans = 0;
for (int i = 1; i <= n; i++) {
int u = -1, MIN = INF;
for (int j = 1; j <= n; j++) {
if (!vis[j] && d[j] < MIN) {
u = j;
MIN = d[j];
}
}
if (u == -1) return -1;
vis[u] = true;
ans += d[u];
for (int j = 0; j < Adj[u].size(); j++) {
int v = Adj[u][j].v;
if (!vis[v] && Adj[u][j].dis < d[v]) {
d[v] = Adj[u][j].dis;
}
}
}
return ans;
}
int main() {
int a, b, dd;
while (scanf("%d", &n) && n) {
for (int i = 1; i <= n; i++) Adj[i].clear();
fill(vis, vis + maxn, false);
for (int i = 0; i < n * (n - 1) / 2; i++) {
scanf("%d%d%d", &a, &b, &dd);
Adj[a].push_back(node(b, dd));
Adj[b].push_back(node(a, dd));
}
int ans = prime();
if (ans > 0) printf("%d\n", ans);
}
return 0;
}
代码(Kruskal)
#include <cstdio>
#include <algorithm>
#define maxE 5000
#define maxV 105
using namespace std;
struct edge {
int u, v;
int dis;
} E[maxE];
int father[maxV];
bool cmp(edge a, edge b) {
return a.dis < b.dis;
}
int findFather(int x) {
if (x == father[x]) return x;
else {
int v = findFather(father[x]);
father[x] = v;
return v;
}
}
int kruskal(int n, int m) {
int ans = 0, num_edge = 0;
for (int i = 1; i <= n; i++) father[i] = i;
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
ans += E[i].dis;
num_edge++;
father[faU] = faV;
if (num_edge == n - 1) break;
}
}
if (num_edge != n - 1) return -1;
else return ans;
}
int main() {
int n, m, a, b, dd;
while (scanf("%d", &n) && n) {
m = n * (n - 1) / 2;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].dis);
}
int ans = kruskal(n, m);
if (ans != -1) printf("%d\n", ans);
}
return 0;
}
B Freckles

题目释义
英文水平不行,没具体看题目内容,大概意思就是:
第一行输入n,表示有n个结点。下面n行为第i个结点的x、y坐标
求把这些点连接起来的最小边权,就是求最小生成树
题目解析
这道题之所以放上来,是因为之前的题目两点间距离都直接告诉我们的,这里需要自己根据sqrt((x1-x2)2+(y1-y2)2)算
哎呦~有点意思🙃
PS:这题中每两个点都能构成一条边,即边有 条,所以边很多,用了Kruskal
代码(Kruskal)
#include <cstdio>
#include <cmath>
#include <algorithm>
#define maxV 105
#define maxE 5000
using namespace std;
struct location {
double x;
double y;
} V[maxV];
struct edge {
int u, v;
double dis;
} E[maxE];
int father[maxV];
bool cmp(edge a, edge b) {
return a.dis < b.dis;
}
int findFather(int x) {
if (x == father[x]) return x;
else {
int v = findFather(father[x]);
father[x] = v;
return v;
}
}
//点数,边数
double kruskal(int n, int m) {
double ans = 0;
int edge_num = 0;
for (int i = 0; i < n; i++) father[i] = i;
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
father[faU] = faV;
ans += E[i].dis;
edge_num++;
if (edge_num == n - 1) break;
}
}
if (edge_num != n - 1) return -1;
else return ans;
}
int main() {
int n;
while (scanf("%d", &n) && n) {
for (int i = 0; i < n; i++) {
scanf("%lf%lf", &V[i].x, &V[i].y);
}
int m = 0;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
E[m].v = i;
E[m].u = j;
E[m].dis = sqrt(pow(V[i].x - V[j].x, 2) + pow(V[i].y - V[j].y, 2));
m++;
}
}
double ans = kruskal(n, m);
if (ans != -1) printf("%.2lf\n", ans);
}
return 0;
}
D 继续畅通工程

题目解析
这道题和A题区别在于有些路径已经建好了,所以在求最小生成树的时候,这条边算edge_num(edge_num==n-1时已形成最小生成树),但是所求的边权中不用加这条边。
代码(Kruskal)
#include <cstdio>
#include <algorithm>
#define maxV 105
#define maxE 5000
using namespace std;
struct edge {
int u, v;
int dis;
} E[maxE];
int father[maxV];
int edge_num;
bool cmp(edge a, edge b) {
return a.dis < b.dis;
}
int findFather(int x) {
if (x == father[x]) return x;
else {
int v = findFather(father[x]);
father[x] = v;
return v;
}
}
int kruskal(int n, int m) {
int ans=0;
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
father[faU] = faV;
edge_num++;
ans += E[i].dis;
if (edge_num == n - 1) break;
}
}
if (edge_num != n - 1) return -1;
else return ans;
}
int main() {
int n, m, temp;
while (scanf("%d", &n) && n) {
m = n * (n - 1) / 2;
edge_num = 0;
for (int i = 1; i <= n; i++) father[i] = i;
for (int i = 0; i < m; i++) {
scanf("%d%d%d%d", &E[i].u, &E[i].v, &E[i].dis, &temp);
if (temp) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
father[faU] = faV;
edge_num++;
}
}
}
int ans=kruskal(n,m);
if(ans!=-1) printf("%d\n",ans);
}
return 0;
}
E Jungle Roads

题目释义
大概意思就是:各个村庄由路相连,以前有很多路,但是需要很多钱维护,否则路会被森林覆盖,现在决定要在保证各个村庄都能到达的情况下使道路维护费用最少。给出各条公路的情况,请输出最少的维护费用。
各条道路用26个大写字母命名,总共不超过75条道路。
第一行输入n表示村庄的数目,后面n-1行依次是各村庄的情况(固定按照26个字母排下来,最后一个村庄不输入)
每行以村庄名开头,后跟次村庄的道路数k,若k>0,后面跟此道路通向的村庄名称和维护费用
题目解析
觉得这道题输入挺新颖,就放上来了,没啥坑感觉
💡这里为了方便,没有按照题目的用字母作为结点的标识,统一将字母-65存int,这样father[]也方便
否则father就要用map<char,char>了;当然father[]直接用65~91也可以,就是觉得有点浪费🙃
代码(Kruskal)
#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxV 30
#define maxE 80
using namespace std;
struct edge {
int u, v;
int dis;
} E[maxE];
int father[maxV];
int findFather(int x) {
if (x == father[x]) return x;
else {
int v = findFather(father[x]);
father[x] = v;
return v;
}
}
bool cmp(edge a, edge b) {
return a.dis < b.dis;
}
int kruskal(int n, int m) {
int ans = 0, edge_num = 0;
for (int i = 0; i < n; i++) father[i] = i;
sort(E, E + m, cmp);
for (int i = 0; i < m; i++) {
int faU = findFather(E[i].u);
int faV = findFather(E[i].v);
if (faU != faV) {
father[faU] = faV;
ans += E[i].dis;
edge_num++;
if (edge_num == n - 1) break;
}
}
if (edge_num != n - 1) return -1;
else return ans;
}
int main() {
int n, m, k, d;
char a, b;
while (scanf("%d", &n) && n) {
m = 0;
for (int i = 0; i < n - 1; i++) {
cin >> a >> k;
while (k--) {
cin >> b >> d;
E[m].v = a - 65;
E[m].u = b - 65;
E[m].dis = d;
m++;
}
}
int ans = kruskal(n, m);
if (ans != -1) printf("%d\n", ans);
}
return 0;
}
本文作者:Joey-Wang
本文链接:https://www.cnblogs.com/joey-wang/p/14541192.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步