11-03最小生成树
最小生成树
一、定义
在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得联通所有结点的的 w(T) 最小,则此 T 为 G 的最小生成树。 w(t) = $\sum_{(u,v)∈t}$w(u,v)最小生成树是最小权值生成树的简称
二、算法Kruskal原理
假设给出了含有n个顶点的连通图,边有若干条,带有自己的权值。1.先构建一个只含有n个顶点,边集为空的子图
2.按照边权大小进行排序,多数从小到大排序
3.按顺序依次选取边,判断两端点是否已经同属于一个集合(并查集)一个集合则不选取该边,非一个集合选取该边
4.直到将所有边选完,合并n-1次即可连通,且因为是排序后从小到大进行的选择,将不会有更优的选择,即可求出最小生成树
三、模板代码实现
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e3 + 10, M = 2e5 + 10;
int n, m, cnt, sum;
int f[N];
struct node {
int x, y, z;
}tr[M];
bool cmp(node u, node v) {
return u.z < v.z;
}
void init(int n) {
for (int i = 1; i <= n; i++)
f[i] = i;
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
f[x] = y;
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> tr[i].x >> tr[i].y >> tr[i].z;
}
sort(tr, tr + m, cmp);
init(n);
cnt = n, sum = 0;
for (int i = 0; i < m; i++) {
int x = tr[i].x;
int y = tr[i].y;
if (find(x) != find(y)) {
merge(x, y);
cnt--;
sum += tr[i].z;
}
}
if (cnt != 1) {
cout << "orz" << endl;
return 0;
}
cout << sum << endl;
return 0;
}
四、相关题
1.P2330 繁忙的都市【普及/提高-】
题意:
给出n,m,表示有n个交叉路口(顶点),m条道路道路(带权边),以下给出m行,每行u,v,c,表示交叉路口u(顶点u)和交叉路口v(顶点v)之间有道路相连,分值为c,需要使得通过选边,使得任意两个交叉路口(顶点)可以互通,同时分值尽可能少,输出选择了几条路,分值最大的那条道路的分值思路:
最小生成树实现:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e3 + 10, M = 2e5 + 10;
int n, m, cnt, sum;
int f[N];
struct node {
int x, y, z;
}tr[M];
bool cmp(node u, node v) {
return u.z < v.z;
}
void init(int n) {
for (int i = 1; i <= n; i++)
f[i] = i;
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
f[x] = y;
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> tr[i].x >> tr[i].y >> tr[i].z;
}
sort(tr, tr + m, cmp);
init(n);
cnt = n, sum = 0;
for (int i = 0; i < m; i++) {
int x = tr[i].x;
int y = tr[i].y;
if (find(x) != find(y)) {
merge(x, y);
cnt--;
sum = tr[i].z;
}
}
cout << n - 1 << ' ' << sum << endl;
return 0;
}
2.CF1245D Shichikuji and Power Grid【普及+/提高】
题意:
点权为 c[i], i和 j 两个城市相连的代价是 k[i]+k[j] 乘上两者的曼哈顿距离求最小代价的方案,给出n,表示n个城市,以下n个城市的坐标,n个点权c[i],n个k[i],输出分三类,首先输出最小代价,换行输出有几个城市建立了发电站,换行输出哪几个城市建立了发电站,输出有几条连线,以下输出每条连线,顺序任意思路:
设虚拟节点,将边权转化为点权,放入vector计入选取的情况数,这里将n+1虚拟节点建立,连接n个节点与第n+1的节点,将点权转化为边权,最小生成树即可实现:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e3 + 10, M = 2e5 + 10;
int n, m, cnt;
long long sum;
int f[N];
struct node {
int x, y;
long long c, k, w;
}tr[M];
struct node1 {
int u, v;
long long w;
}tr1[N * N];
vector<node1> ans;
bool cmp(node1 u, node1 v) {
return u.w < v.w;
}
void init(int n) {
for (int i = 1; i <= n; i++)
f[i] = i;
}
int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
f[x] = y;
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> tr[i].x >> tr[i].y;
}
for (int i = 1; i <= n; i++) {
cin >> tr[i].c;
}
for (int i = 1; i <= n; i++) {
cin >> tr[i].k;
}
cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (i == j){
tr1[cnt++] = {i, n + 1, tr[i].c};
} else {
tr1[cnt++] = {i, j, (tr[i].k + tr[j].k) * (abs(tr[i].x - tr[j].x) + abs(tr[i].y - tr[j].y))};
}
}
}
sort(tr1, tr1 + cnt, cmp);
init(n+1);
sum = 0;
int sum1 = 0, sum2 = 0;
for (int i = 0; i < cnt; i++) {
int x = tr1[i].u;
int y = tr1[i].v;
}
for (int i = 0; i < cnt; i++) {
int x = tr1[i].u;
int y = tr1[i].v;
if (find(x) != find(y)) {
merge(x, y);
sum += tr1[i].w;
ans.push_back(tr1[i]);
if (y == n + 1) sum1++;
sum2++;
}
}
cout << sum << endl;
cout << sum1 << endl;
for (int i = 0; i < ans.size(); i++) {
if (ans[i].v == n + 1) {
cout << ans[i].u << ' ';
}
}cout << endl;
cout << sum2 - sum1 << endl;
for (int i = 0; i < ans.size(); i++) {
if (ans[i].v != n + 1) {
cout << ans[i].u << ' ' << ans[i].v << endl;;
}
}
return 0;
}
浙公网安备 33010602011771号