【kuangbin】专题六 最小生成树
【kuangbin】专题六 最小生成树
https://www.acwing.com/activity/content/90/
感觉这个专题难度偏低
个人认为只有第7. 11.可以写一写,其它都是板子题
1. AcWing 4291. 丛林之路
最小生成树板子题
kruscal
#include <bits/stdc++.h>
using namespace std;
const int N = 30, M = 80;
int n, k;
int fa[M];
struct Node {
int a, b, w;
bool operator<(const Node &t) const {
return w < t.w;
}
}e[M << 1];
void init () {
for (int i = 0; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
//保证连通
init ();
sort (e, e + k);
int ans = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find(b);
if (a != b) {
fa[a] = b;
ans += w;
}
}
return ans;
}
int main () {
while (cin >> n, n) {
k = 0, n --;
for (int i = 0; i < n; i ++) {
char id; cin >> id;
int a = id - 'A';
int m; cin >> m;
while (m --) {
int w;
cin >> id >> w;
int b = id - 'A';
e[k++] = {a, b, w}, e[k++] = {b, a, w};
}
}
cout << kruscal () << endl;
}
}
//就是求最小生成树
2. AcWing 4292. 网络连接
水水水
#include<bits/stdc++.h>
using namespace std;
const int N = 55, M = 1505;
int n, m, k;
int fa[N];
struct Node {
int a, b, w;
bool operator<(const Node &t) const {
return w < t.w;
}
}e[M << 1];
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
}
}
return ans;
}
int main () {
while (cin >> n, n) {
cin >> m;
k = 0;
while (m --) {
int a, b, w;
cin >> a >> b >> w;
// bool flag = true;
// for (int i = 0; i < k; i ++) {
// if (e[i].a == a && e[i].b == b && e[i].w < w) {
// flag = false;
// break;
// }
// }
// if (flag)
e[k ++] = {a, b, w}, e[k ++] = {b, a, w};
}
cout << kruscal () << endl;
}
}
//图中可能存在重边。
3. AcWing 4293. 建造空间站
抽象建图:
对于本来就已经相交或相切的圆,建一条权值为0的边;
对于相离的圆,边权为\(|O_1 O_2|-r_1-r_2\)
建完图之后,跑最小生成树即可
注意一些double的问题
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, k;
int fa[N];
struct zuobiao {
double x, y, z, r;
}a[N];
struct Node {
int a, b;
double w;
bool operator < (const Node &t) const {
return w < t.w;
}
}e[N*N*2];
void init () {
for (int i = 1; i <= N; i ++) //注意这里的更新是N
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
double kruscal () {
init ();
sort (e, e + k);
double ans = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b;
double w = e[i].w; //分开来!!
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
}
}
return ans;
}
int main () {
int n;
while (cin >> n , n) {
k = 0;
for (int i = 1; i <= n; i ++) {
double x, y, z, r;
cin >> x >> y >> z >> r;
a[i] = {x, y, z, r};
}
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
double x = a[i].x, y = a[i].y, z = a[i].z, r = a[i].r;
double xx = a[j].x, yy = a[j].y, zz = a[j].z, rr = a[j].r;
double d1 = (x-xx)*(x-xx) + (y-yy)*(y-yy) + (z-zz)*(z-zz), d2 = r + rr;
if (d1 <= d2*d2) e[k ++] = {i, j, 0}, e[k ++] = {j, i, 0};
else {
double w = sqrt (d1) - d2;
e[k ++] = {i, j, w}, e[k ++] = {j, i, w};
}
}
cout << fixed << setprecision(3) << kruscal () << endl;
}
}
//维系两个相离的球的通道长度为|AB|-r1-r2
//相交或相切就建一条为0的边
//然后跑最小生成树
4. AcWing 4294. 修建道路
和上一题真的蛮像的
就是已有的边权值为0,然后没连上的就加权值
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = N*N*2;
int n, m, k;
bool st[N][N]; //表示已有边
int dis[N][N], fa[N];
struct Node {
int a, b, w;
bool operator< (const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 1; i <= N; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
}
}
return ans;
}
int main () {
cin >> n;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
cin >> dis[i][j];
cin >> m;
while (m --) {
int a, b;
cin >> a >> b;
st[a][b] = st[b][a] = true;
}
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
if (st[i][j] || st[j][i]) e[k++] = {i, j, 0}, e[k++] = {j, i, 0};
//需要自己建边
else e[k++] = {i, j, dis[i][j]}, e[k++] = {j, i, dis[j][i]};
}
cout << kruscal () << endl;
}
//每个都建边,如果是已有边,则权值为0
5. AcWing 4295. QS网络
TLE了老半天都不知道咋回事,后来才发现是要记录一下连边的数量,全联通了就退出
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = N*N*2;
int n, k;
int p[N], fa[N];
struct Node {
int a, b, w;
bool operator < (const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 1; i <= n; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0, cnt = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b =find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == n + 1) break;
}
return ans;
}
void solve () {
k = 0;
cin >> n;
for (int i = 1; i <= n; i ++) cin >> p[i];
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++) {
int x; cin >> x;
int w = p[i] + p[j] + x;
e[k ++] = {i, j, w};
}
// for (int i = 1; i <= n; i ++)
// for (int j = i + 1; j <= n; j ++) {
// int w = p[i] + p[j] + dis[i][j];
// e[k ++] = {i, j, w}, e[k++] = {j, i, w};
// }
cout << kruscal () << endl;
}
int main () {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t; cin >> t;
while (t --) solve ();
}
//不仅有边权还有点权
//直接在建边的时候顺便把点权加到边上
//联通了就提前退出!!
6. AcWing 4332. 卡车历史
和上一道题目是一样的问题
连通了就提前退出
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, M = N*N*2;
string s[N];
int n, k, fa[N];
struct Node {
int a, b, w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
int count (string p, string t) {
int cnt = 0;
for (int i = 0; i < p.size(); i ++)
if (p[i] != t[i]) cnt ++;
return cnt;
}
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0, cnt = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == n + 1) break;
}
return ans;
}
void solve () {
k = 0;
for (int i = 1; i <= n; i ++) cin >> s[i];
//建图
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
e[k++] = {i, j, count(s[i], s[j])};
}
cout << "The highest possible quality is 1/" << kruscal () << ".\n";
}
int main () {
while (cin >> n, n) solve ();
}
//也是两两建图
7. AcWing 387. 北极网络
求子图中最小生成树的最长边
子图的最小生成树有 n−s−1 条边,另外还需要一条边连到“带有卫星的节点”
即cnt==n-s时退出
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 505, M = N*N*2;
typedef pair<int, int> pii;
int n, s, k;
pii p[N];
int fa[N];
struct Node {
int a, b;
double w;
bool operator< (const Node &t) const {
return w < t.w;
}
}e[M];
double count (int i, int j) {
return sqrt ((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}
void init () {
for (int i = 0; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
double kruscal () {
init ();
sort (e, e + k);
int cnt = 0;
double ans = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b;
double w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans = max (ans, w);
cnt ++;
}
if (cnt == n - s) break;
}
return ans;
}
void solve () {
k = 0;
cin >> s >> n;
for (int i = 0; i < n; i ++)
cin >> p[i].x >> p[i].y;
for (int i = 0; i < n; i ++)
for (int j = i + 1; j < n; j ++) {
e[k++] = {i, j, count(i, j)};
e[k++] = {j, i, count(i, j)};
}
//注意特判
if (n <= s) {
cout << "0.00\n";
return ;
}
cout << fixed << setprecision(2) << kruscal() << endl;
}
int main () {
int t; cin >> t;
while (t --) solve ();
}
//求子图中最小生成树的最长边
//子图的最小生成树有 n−s−1 条边,另外还需要一条边连到“带有卫星的节点”
//即cnt==n-s时退出
8. AcWing 4333. 高速公路
求最小生成树+记录方案
已有的就权值为0
无需统计距离
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair <int, int> pii;
const int N = 755, M = N*N*2+2005;
int n, m, k;
pii p[N];
int fa[N];
struct Node {
int a, b;
double w;
bool operator < (const Node &t) const {
return w < t.w;
}
}e[M];
double count (int i, int j) {
return sqrt ((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y));
}
void init () {
for (int i = 1; i <= n; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
void kruscal () {
init();
sort (e, e + k);
int cnt = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b;
double w = e[i].w;
//if (w == 0) continue;
//a = find (a), b = find (b);
if (find(a) != find(b)) {
fa[find(a)] = find(b);
cnt ++;
if (w) cout << a << ' ' << b << endl;
}
if (cnt == n - 1) break;
}
}
int main () {
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> p[i].x >> p[i].y;
cin >> m;
while (m --) {
int x, y;
cin >> x >> y;
e[k++] = {x, y, 0}, e[k++] = {y, x, 0};
}
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
double w = count(i, j);
e[k ++] = {i, j, w}, e[k ++] = {j, i, w};
}
kruscal();
}
//求最小生成树+记录方案
//已有的就权值为0
//无需统计距离
9. AcWing 4334. 农场网络
板子T
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = N*N*2;
int n, k;
int fa[N];
struct Node {
int a, b, w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 1; i <= n; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0, cnt = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == n - 1) break;
}
return ans;
}
int main () {
while (cin >> n) {
k = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++) {
int x; cin >> x;
if (x) e[k++] = {i, j, x}, e[k++] = {j, i, x};
}
cout << kruscal () << endl;
}
}
//和前面几题一模一样
10. AcWing 4335. 博格迷宫
先预处理距离
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 105, M = N*N*2;
int n, m, k, tot;
string tg[55];
int g[55][55], dis[55][55];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int fa[M];
bool Range (int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= m) return false;
return true;
}
struct Node {
int a, b, w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 0; i <= n*m; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort (e, e + k);
int ans = 0, cnt = 0;
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == tot - 1) break;
}
return ans;
}
void bfs (int sx, int sy) {
memset (dis, 0x3f, sizeof dis);
dis[sx][sy] = 0;
queue <pii> q;
q.push ({sx, sy});
while (!q.empty()) {
auto t = q.front();
q.pop();
int x = t.first, y = t.second;
for (int i = 0; i < 4; i ++) {
int xx = x + dx[i], yy = y + dy[i];
if (!Range (xx, yy) || tg[xx][yy] == '#' || dis[xx][yy] != 0x3f3f3f3f) continue;
q.push({xx, yy});
dis[xx][yy] = dis[x][y] + 1;
if (g[xx][yy] > 0) {
e[k++] = {g[sx][sy], g[xx][yy], dis[xx][yy]};
}
}
}
}
void solve () {
k = 0, tot = 0;
cin >> m >> n;
getchar();
for(int i = 0 ; i < n ; i ++)
getline(cin, tg[i]);
memset(g, 0, sizeof g);
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++) {
if (tg[i][j] == 'S' || tg[i][j] == 'A')
g[i][j] = ++ tot;
}
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++) {
if (tg[i][j] == 'S' || tg[i][j] == 'A')
bfs (i, j);
}
cout << kruscal () << endl;
}
int main () {
int t; cin >> t;
while (t --) solve ();
}
//先预处理距离
//注意空格的输入要特殊处理
//转化,坐标变为当前出现过多少次
11. AcWing 4336. 唯一最小生成树
最小生成树 不唯一的情况:
当且仅当树外有一条 长度一样 的边可以替代树上的某一条边
res统计连续段边长相等的
与最小生成树ans比较
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = N*N*2;
int n, m, k;
int fa[N];
struct Node {
int a, b, w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 1; i <= n; i ++) fa[i] = i;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
// int kruscal () {
// init ();
// //sort (e, e + k);
// int cnt = 0, ans = 0;
// for (int i = 0; i < k; i ++) {
// int a = e[i].a, b = e[i].b, w = e[i].w;
// a = find (a), b = find (b);
// if (a ^ b) {
// fa[a] = b;
// ans += w;
// cnt ++;
// }
// if (cnt == n - 1) break;
// }
// return ans;
// }
void solve () {
k = 0;
cin >> n >> m;
init ();
while (m --) {
int a, b, w;
cin >> a >> b >> w;
e[k++] = {a, b, w};
}
sort (e, e + k);
int res = 0, ans = 0;
for (int i = 0; i < k; i ++) {
int r = i;
while (r < k && e[i].w == e[r].w) r ++;
r --;
for (int j = i; j <= r; j ++) {
int a = find (e[j].a), b = find (e[j].b);
if (a != b) res += e[j].w;
}
for (int j = i; j <= r; j ++) {
int a = find (e[j].a), b = find (e[j].b);
if (a != b) {
fa[a] = b;
ans += e[j].w;
}
}
i = r;
}
if (ans == res) cout << ans << endl;
else cout << "Not Unique!\n";
}
int main () {
int t; cin >> t;
while (t --) solve ();
}
//最小生成树 不唯一的情况:
//当且仅当树外有一条 长度一样 的边可以替代树上的某一条边
12. AcWing 4337. 还是畅通工程
板题
#include<bits/stdc++.h>
using namespace std;
const int N = 105, M = N*N*2;
int fa[N], n, m;
struct Node {
int a, b, w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i;
}
int find(int x) {
if(x != fa[x])
fa[x]=find(fa[x]);
return fa[x];
}
int kruscal () {
init ();
sort(e + 1, e + 1 + m);
int ans = 0, cnt = 0;
for (int i = 1; i <= m; i ++) {
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find (a), b = find (b);
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == n - 1) break;
}
return ans;
}
int main() {
while(cin >> n, n) {
m = n * (n - 1) / 2;
for(int i = 1; i <= m; i ++)
cin >> e[i].a >> e[i].b >> e[i].w;
cout << kruscal () << endl;
}
return 0;
}
13. AcWing 4338. 畅通工程再续
注意浮点数比大小的问题
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 105, M = N*N*2;
const double eps = 1e-9;
int fa[N], n, k;
pii p[N];
struct Node {
int a, b;
double w;
bool operator <(const Node &t) const {
return w < t.w;
}
}e[M];
double count (int i, int j) {
return sqrt ((p[i].first-p[j].first)*(p[i].first-p[j].first) + (p[i].second-p[j].second)*(p[i].second-p[j].second));
}
void init () {
for (int i = 1; i <= n; i ++)
fa[i] = i;
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
double kruscal () {
init ();
double ans = 0;
int cnt = 0;
sort (e, e + k);
for (int i = 0; i < k; i ++) {
int a = e[i].a, b = e[i].b;
a = find (a), b = find (b);
double w = e[i].w;
if (a != b) {
fa[a] = b;
ans += w;
cnt ++;
}
if (cnt == n - 1) break;
}
if (cnt < n - 1) return -1;
return ans * 100;
}
void solve () {
k = 0;
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> p[i].first >> p[i].second;
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++) {
double w = count (i, j);
if (w >= 10 - eps && w <= 1000 - eps)
e[k++] = {i, j, count (i, j)};
}
double ans = kruscal();
if (ans == -1) cout << "oh!\n";
else cout << fixed << setprecision(1) << ans << endl;
}
int main () {
int t;
cin >> t;
while (t --) {
solve ();
}
}
//>=10 && <= 1000的才放入集合里