[kuangbin带你飞]专题六 最小生成树
Krusal 时间复杂度 \(O(m\log m)\)
Prim 时间复杂度 \(O(n^2)\),二叉堆优化至 \(O(m\log n)\)。Prim 主要用于稠密图。
1. POJ1251 Jungle Roads
题意:给出 n 个村庄,n - 1 个村庄连接的村庄以及道路及其每月维护费,求使村庄连通的每月最小花费。
模板题,上网查了才发现一直 RE 是 scanf 的锅。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 30, M = 1005;
struct edge {
int u, v, w;
edge() {}
edge(int u, int v, int w) : u(u), v(v), w(w) {}
friend bool operator < (edge a, edge b) {
return a.w < b.w;
}
}e[M];
int n, m;
int fa[N];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int krusal() {
sort(e + 1, e + m + 1);
int ans = 0, cnt = 0;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
int x = find(e[i].u);
int y = find(e[i].v);
if (x != y) {
ans += e[i].w;
fa[x] = y;
cnt++;
}
if (cnt == n - 1) break;
}
if (cnt < n - 1) return -1;
return ans;
}
int main()
{
while (scanf("%d", &n), n) {
m = 0;
for (int i = 1; i < n; ++i) {
char c;
int k, x, y;
cin >> c >> k;
x = c - 'A' + 1;
while (k--) {
cin >> c >> y;
e[++m] = edge(x, c - 'A' + 1, y);
}
}
printf("%d\n", krusal());
}
return 0;
}
2. POJ1287 Networking
题意:在一个区域内装网络,给定一系列点和可能的电缆,使任意点连接求电缆长度最小值。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 55, M = 1505;
struct edge {
int u, v, w;
edge() {}
edge(int u, int v, int w) : u(u), v(v), w(w) {}
friend bool operator < (edge a, edge b) {
return a.w < b.w;
}
}e[M];
int n, m;
int fa[N];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int krusal() {
sort(e + 1, e + m + 1);
int ans = 0, cnt = 0;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
int x = find(e[i].u);
int y = find(e[i].v);
if (x != y) {
ans += e[i].w;
fa[x] = y;
cnt++;
}
if (cnt == n - 1) break;
}
if (cnt < n - 1) return -1;
return ans;
}
int main()
{
while (scanf("%d", &n), n) {
scanf("%d", &m);
int x, y, z;
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &x, &y, &z);
e[i] = edge(x, y, z);
}
printf("%d\n", krusal());
}
return 0;
}
3. POJ2031 Building a Space Station
题意:三维空间中给出 n 个球体的坐标和半径,如果球体未相通,需要建立一些通道连接所有球体,表面相切可认为相通,求通道的最短距离。
任意两个球相交距离为0,不相交圆心之间距离减去半径和为距离,用最小生成树做即可,G++ 用 double 会 Wa
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int N = 105;
const double INF = 0x3f3f3f3f;
struct point {
double x, y, z;
double r;
}p[N];
int n;
double a[N][N];
double d[N];
int v[N];
double dis(point a, point b) {
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z));
}
void prim() {
for (int i = 1; i <= n; ++i) d[i] = INF;
memset(v, 0, sizeof(v));
d[1] = 0;
for (int i = 1; i < n; ++i) {
int x = 0;
for (int j = 1; j <= n; ++j)
if (!v[j] && (x == 0 || d[j] < d[x])) x = j;
v[x] = 1;
for (int y = 1; y <= n; ++y)
if (!v[y]) d[y] = min(d[y], a[x][y]);
}
}
int main()
{
while (scanf("%d", &n), n) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) a[i][j] = INF;
for (int i = 1; i <= n; ++i) a[i][i] = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z, &p[i].r);
}
for (int i = 1; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
a[i][j] = a[j][i] = max(0.0, dis(p[i], p[j]) - p[i].r - p[j].r);
}
}
prim();
double ans = 0;
for (int i = 2; i <= n; ++i) ans += d[i];
printf("%.3lf\n", ans);
}
return 0;
}
4. POJ2421 Constructing Roads
题意:给出 \(n\) 个点和 \(n\times n\) 的邻接矩阵,q 条已连好的边,求使点连通还需建立道路长度最小值。
适合用 krusal,已经连通的点先合并。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 105;
struct edge {
int u, v, w;
friend bool operator < (edge a, edge b) {
return a.w < b.w;
}
}e[N * N];
int n, tot, q, cnt;
int fa[N];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx == fy) return;
fa[fx] = fy;
cnt++;
}
int krusal() {
sort(e + 1, e + tot + 1);
int ans = 0;
for (int i = 1; i <= tot; ++i) {
if (find(e[i].u) != find(e[i].v)) {
unite(e[i].u, e[i].v);
ans += e[i].w;
}
if (cnt == n - 1) break;
}
if (cnt < n - 1) return -1;
return ans;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &e[++tot].w);
e[tot].u = i;
e[tot].v = j;
}
}
scanf("%d", &q);
int a, b;
while (q--) {
scanf("%d%d", &a, &b);
unite(a, b);
}
printf("%d\n", krusal());
return 0;
}
5. ZOJ1586 QS Network
题意:有一种叫 QS 的智能生物,两个 QS 想要连接,需要购买两个网络适配器和一段网络电缆,一个网络适配器只能只能在单个连接中使用。通信过程中,QS 将消息广播到它所有连接的 QS,接收到消息的 QS 将消息广播到所有连接的 QS,直到所有 QS 收到消息。给出 QS 数目 n,每个 QS 网络适配器价格,\(n\times n\) 矩阵,i 行 j 列 表示 i,j 之间电缆费用,求连通 n 个点最小花费。
网络适配器价格加入边中,用 prim。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 1005;
int t, n;
int a[N], g[N][N];
int d[N], v[N];
int prim() {
priority_queue<P, vector<P>, greater<P> > q;
memset(d, 0x3f, sizeof(d));
memset(v, 0, sizeof(v));
int ans = 0;
d[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (v[x]) continue;
v[x] = 1;
ans += d[x];
for (int y = 1; y <= n; ++y) {
if (!v[y] && d[y] > g[x][y]) {
d[y] = g[x][y];
q.push(P(d[y], y));
}
}
}
return ans;
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &g[i][j]);
g[i][j] += a[i] + a[j];
}
}
printf("%d\n", prim());
}
return 0;
}
6. POJ1789 Truck History
题意:7 个小写字母表示一个编码,编码距离的定义为 7 个位置上不同字符的个数,n 个编码,求连接所有编码的最短距离。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 2005;
int n;
int d[N], v[N], a[N][N];
char str[N][10];
void prim() {
memset(d, 0x3f, sizeof(d));
memset(v, 0, sizeof(v));
d[1] = 0;
for (int i = 1; i < n; ++i) {
int x = 0;
for (int j = 1; j <= n; ++j)
if (!v[j] && (x == 0 || d[j] < d[x])) x = j;
v[x] = 1;
for (int y = 1; y <= n; ++y)
if (!v[y]) d[y] = min(d[y], a[x][y]);
}
}
int main()
{
while (scanf("%d", &n), n) {
for (int i = 1; i <= n; ++i) {
scanf("%s", str[i]);
}
for (int i = 1; i <= n; ++i) {
a[i][i] = 0;
for (int j = i + 1; j <= n; ++j) {
int cnt = 0;
for (int k = 0; k < 7; ++k)
if (str[i][k] != str[j][k]) cnt++;
a[i][j] = a[j][i] = cnt;
}
}
prim();
int ans = 0;
for (int i = 1; i <= n; ++i) ans += d[i];
printf("The highest possible quality is 1/%d.\n", ans);
}
return 0;
}
7. POJ2349 Arctic Network
题意:s 个卫星频道,p 个前哨站,给出前哨站坐标,两个前哨站可以通过卫星频道或无线电通信,卫星频道无视距离,无线电两者距离不能超过 d,所有前哨站可以直接或间接通信情况下,求 d 的最小值。
最小生成树求出 p - 1 条边从小到大排序,后 s - 1 条用卫星通信,输出第 p - s 条边,即 d[n - m + 1]。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int N = 505;
const double INF = 0x3f3f3f3f;
struct point {
int x, y;
}p[N];
int t, m, n;
double g[N][N];
double d[N];
int v[N];
double dis(point a, point b) {
return sqrt((a.x - b.x) * (a.x - b.x) / 1.00 + (a.y - b.y) * (a.y - b.y) / 1.0);
}
void prim() {
for (int i = 0; i <= n; ++i) d[i] = INF;
memset(v, 0, sizeof(v));
d[1] = 0.0;
for (int i = 1; i < n; ++i) {
int x = 0;
for (int j = 1; j <= n; ++j)
if (!v[j] && (x == 0 || d[j] < d[x])) x = j;
v[x] = 1;
for (int y = 1; y <= n; ++y)
if (!v[y]) d[y] = min(d[y], g[x][y]);
}
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
}
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
double l = dis(p[i], p[j]);
g[i][j] = g[j][i] = l;
}
}
prim();
sort(d + 1, d + n + 1);
printf("%.2lf\n", d[n - m + 1]);
}
return 0;
}
8. POJ1751 Highways
题意:给出 n 个城镇及其坐标,已经修好的 m 条告诉公路,为使所有城镇连通,求还需修建高速公路的最小长度。
已存在的边权为 0,用 pre 记录相邻结点。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<double, int> P;
const int N = 755;
const double INF = 0x3f3f3f3f, eps = 1e-8;
int n, m;
int x[N], y[N], vis[N], pre[N];
double g[N][N], d[N];
void prim() {
priority_queue<P, vector<P>, greater<P> > q;
for (int i = 1; i <= n; ++i) d[i] = INF;
memset(vis, 0, sizeof(vis));
d[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (!vis[u]) vis[u] = 1;;
for (int v = 1; v <= n; ++v) {
if (!vis[v] && d[v] > g[u][v]) {
d[v] = g[u][v];
pre[v] = u;
q.push(P(d[v], v));
}
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d", &x[i], &y[i]);
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
double l = sqrt(1.0 * (x[i] - x[j]) * (x[i] - x[j]) + 1.0 * (y[i] - y[j]) * (y[i] - y[j]));
g[i][j] = g[j][i] = l;
}
}
scanf("%d", &m);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
g[a][b] = g[b][a] = 0.0;
}
prim();
for (int i = 2; i <= n; ++i) {
if (g[pre[i]][i] > eps) {
printf("%d %d\n", pre[i], i);
}
}
return 0;
}
9. POJ1258 Agri-Net
题意:\(n\times n(3\le n\le100)\) 的邻接矩阵,表示 \(n\) 个村庄之间的距离,Farmer Jorn 要把所有村庄连接起来,求最短距离。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 105;
int n;
int a[N][N];
int d[N], v[N];
int prim() {
priority_queue<P, vector<P>, greater<P> > q;
memset(d, 0x3f, sizeof(d));
memset(v, 0, sizeof(v));
int ans = 0;
d[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (v[x]) continue;
v[x] = 1;
ans += d[x];
for (int y = 1; y <= n; ++y) {
if (!v[y] && d[y] > a[x][y]) {
d[y] = a[x][y];
q.push(P(d[y], y));
}
}
}
return ans;
}
int main()
{
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
scanf("%d", &a[i][j]);
}
}
printf("%d\n", prim());
}
return 0;
}
10. POJ3026 Borg Maze
题意:Borg 是一种强大的类机器人种族,在迷宫中帮助 Borg 找出最小的距离同化外星人,每当一个外星人被同化或者在搜索起点,集团可能会分裂成两个或以上集团。
bfs 每个 A 点与 S 点到其他点距离建图,之后用 prim 即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 105, M = 55;
struct node {
int x, y, step, id;
node() {}
node(int x, int y, int step, int id) : x(x), y(y), step(step) {}
}p[N];
int t, m, n, cnt;
int d[N], g[N][N], vis2[N];
int vis1[M][M], mp[M][M];
char s[M][M];
int dx[] = {1, -1, 0, 0};
int dy[] = {0 , 0, 1, -1};
void bfs(int st) {
memset(vis1, 0, sizeof(vis1));
queue<node> q;
q.push(p[st]);
vis1[p[st].x][p[st].y] = 1;
while (!q.empty()) {
node u = q.front();
q.pop();
g[st][mp[u.x][u.y]] = u.step;
for (int i = 0; i < 4; ++i) {
int tx = u.x + dx[i];
int ty = u.y + dy[i];
if (tx > 0 && tx <= n && ty > 0 && ty <= m && s[tx][ty] != '#' && !vis1[tx][ty]) {
q.push(node(tx, ty, u.step + 1));
vis1[tx][ty] = 1;
}
}
}
}
int prim() {
memset(d, 0x3f, sizeof(d));
memset(vis2, 0, sizeof(vis2));
priority_queue<P, vector<P>, greater<P> > q;
int ans = 0;
d[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
int u = q.top().second;
q.pop();
if (!vis2[u]) {
vis2[u] = 1;
ans += d[u];
}
for (int v = 1; v <= cnt; ++v) {
if (!vis2[v] && d[v] > g[u][v]) {
d[v] = g[u][v];
q.push(P(d[v], v));
}
}
}
return ans;
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d%d", &m, &n);
memset(mp, 0, sizeof(mp));
cnt = 0;
gets(s[0]);
for (int i = 1; i <= n; ++i) {
gets(s[i] + 1);
for (int j = 1; j <= m; ++j) {
if (s[i][j] == 'A' || s[i][j] == 'S') {
p[++cnt] = node(i, j, 0);
mp[i][j] = cnt;
}
}
}
for (int i = 1; i <= cnt; ++i) {
bfs(i);
}
printf("%d\n", prim());
}
return 0;
}
11. POJ1679 The Unique MST
题意:给定连通无向图,判断最小生成树是否唯一。
枚举最小生成树的每一条边,删除后求最小生成树,结果相同不唯一,删除边后 krusal 里 cnt < n - 1 判断图是否为树,不是最小生成树唯一。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 0x3f3f3f3f, N = 105, M = 1e4 + 5;
struct edge {
int u, v, w;
friend bool operator < (edge a, edge b) {
return a.w < b.w;
}
}e[M];
int t, n, m;
int fa[N], sta[N], top;
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int krusal(int num) {
int cnt = 0, ans = 0, tot = 0;
for (int i = 1; i <= n; ++i) fa[i] = i;
for (int i = 1; i <= m; ++i) {
if (i == num) continue;
int x = find(e[i].u);
int y = find(e[i].v);
if (x != y) {
fa[x] = y;
ans += e[i].w;
cnt++;
if (!num) sta[++top] = i;
}
if (cnt == n - 1) break;
}
if (cnt < n - 1) return -1;
return ans;
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
}
sort(e + 1, e + m + 1);
top = 0;
int ans = krusal(0);
int flag = 0;
for (int i = 1; i <= top; ++i) {
int res = krusal(sta[i]);
if (res == ans) { flag = 1; break; }
}
if (flag) puts("Not Unique!");
else printf("%d\n", ans);
}
return 0;
}
12. HDU1233 还是畅通工程
题意:求最小生成树。
模板题,完全图用 prim。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f, N = 105;
int n, m;
int d[N], vis[N], g[N][N];
int prim() {
memset(d, 0x3f, sizeof(d));
memset(vis, 0, sizeof(vis));
priority_queue<P, vector<P>, greater<P> > q;
int ans = 0;
d[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (!vis[x]) {
vis[x] = 1;
ans += d[x];
}
for (int y = 1; y <= n; ++y) {
if (!vis[y] && d[y] > g[x][y]) {
d[y] = g[x][y];
q.push(P(d[y], y));
}
}
}
return ans;
}
int main()
{
while (scanf("%d", &n), n) {
m = n * (n - 1) / 2;
int x, y, z;
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &x, &y, &z);
g[x][y] = g[y][x] = z;
}
printf("%d\n", prim());
}
return 0;
}
13. HDU1301 Jungle Roads
和第一题完全一样。
14. HDU1875 畅通工程再续
题意:给出 n 个小岛坐标,在小岛间建桥连通所有岛,条件是小岛间距离属于 \([10,1000]\),价格 100 元/米,求最小造价。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
typedef pair<double, int> P;
const int N = 105;
const double INF = 0x3f3f3f3f;
struct point {
int x, y;
}p[N];
int t, n;
int vis[N];
double d[N], g[N][N];
double dis(point a, point b) {
return sqrt(1.0 * (a.x - b.x) * (a.x - b.x) + 1.0 * (a.y - b.y) * (a.y - b.y));
}
double prim() {
for (int i = 0; i <= n; ++i) d[i] = INF;
memset(vis, 0, sizeof(vis));
priority_queue<P, vector<P>, greater<P> > q;
double ans = 0;
d[1] = 0.0;
q.push(P(0.0, 1));
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (!vis[x]) {
vis[x] = 1;
ans += d[x];
}
for (int y = 1; y <= n; ++y) {
if (!vis[y] && d[y] > g[x][y] && g[x][y] >= 10) {
d[y] = g[x][y];
q.push(P(d[y], y));
}
}
}
return ans;
}
int main()
{
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
}
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
double len = dis(p[i], p[j]);
if (len >= 10 && len <= 1000) {
g[i][j] = g[j][i] = len;
}
}
}
double ans = prim() * 100;
if (!ans) puts("oh!");
else printf("%.1lf\n", ans);
}
return 0;
}

浙公网安备 33010602011771号