专题名称:kuangbin带你飞
专题链接: [kuangbin带你飞]专题1-23 - Virtual Judge (vjudge.net)
题型分类:
最小生成树基础:1 2 4 5 6 9 12 13
最小生成树应用:10 3 8 7
次小生成树:11
最小生成树模板
最小生成树分别有两个模板
prim算法(适用于稠密图),时间复杂度O(n^2+m) [可以用堆优化达到O(mlongn)]
int n;
int g[N][N];
int dist[N];
bool vis[N];
int prim(){
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i++){
int t = -1;
for (int j = 1; j <= n; j++)
if (!vis[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
vis[t] = true;
for (int j = 1; j <= n; j++) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
Kruskal算法(适用于稀疏图),时间复杂度O(mlogn)
int n, m;
int p[N];
struct Edge{
int a, b, w;
bool operator<(const Edge &W) const{
return w < W.w;
}
} edges[M];
int find(int x){
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal(){
sort(edges, edges + m);
for (int i = 1; i <= n; i++) p[i] = i;
int res = 0;//, cnt = 0;
for (int i = 0; i < m; i++){
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b){
p[a] = b;
res += w;
//cnt++;
}
}
//if (cnt < n - 1) return INF;
return res;
}
次小生成树模板
可参考第11题
1.POJ-1251 Jungle Roads
最小生成树模板题,主要把字符型转化为整型
#include <iostream>
#include <algorithm>
const int N = 10010;
const int inf = 0x3f3f3f3f;
int n, m, p[N], res;
struct Edge {
int a, b, w;
} edges[N];
bool cmp(Edge i, Edge j) {
return i.w < j.w;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
while (std::cin >> n) {
if (n == 0) break;
int cnt = 0;
for (int i = 0; i < n - 1; i++) {
char c;
int t;
int front;
std::cin >> c >> t;
front = c - 'A' ;
while (t--) {
char cend;
int trail;
int wealth;
std::cin >> cend >> wealth;
trail = cend - 'A' ;
// edges[cnt++] = {front, trail, wealth};
edges[cnt].a = front;
edges[cnt].b = trail;
edges[cnt++].w = wealth;
}
}
std::sort(edges, edges + cnt, cmp);
for (int i = 0; i < n; i++) p[i] = i;
res = 0;
for (int i = 0; i < cnt; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b) {
p[a] = b;
res += w;
}
}
std::cout << res << "\n";
}
return 0;
}
2.POJ-1287 Networking
模板题。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
const int N = 10010;
const int inf = 0x3f3f3f3f;
int n, m, p[N];
struct Edge {
int a, b, w;
} edges[N];
bool cmp(Edge i, Edge j) {
return i.w < j.w;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
while (std::cin >> n, n) {
std::cin >> m;
for (int i = 0; i < m; i++) {
int a, b, w;
std::cin >> a >> b >> w;
edges[i].a = a, edges[i].b = b, edges[i].w = w;
}
std::sort(edges, edges + m, cmp);
for (int i = 1; i <= n; i++) p[i] = i;
int res = 0;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b) {
p[a] = b;
res += w;
}
}
std::cout<<res<<"\n";
}
return 0;
}
3.POJ-2031 Building a Space Station
这题有一个值得注意的点就是,若两圆心距离-半径之和<=0则权值设为0
还有一个坑点:%lf记得交C++,%f交G++
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 105;
struct node{
int u, v;
double w;
}a[N * N], temp;
int n, p[N];
double x[N], y[N], z[N], r[N];
bool vis[N];
int find(int x){
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
double dist(int i, int j){
double dx = x[i] - x[j], dy = y[i] - y[j], dz = z[i] - z[j];
return sqrt(dx * dx + dy * dy + dz * dz) - r[i] - r[j];
}
bool cmp(node i, node j){
return i.w < j.w;
}
int main(){
while(~scanf("%d", &n), n){
for(int i = 1; i <= n; i++){
scanf("%lf%lf%lf%lf", &x[i], &y[i], &z[i], &r[i]);
p[i] = i;
}
if(n == 1) {
puts("0.000");
continue;
}
int t = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
double w = dist(i, j);
if(w < 0) w = 0;
temp.u = i, temp.v = j, temp.w = w;
a[++t] = temp;
}
}
sort(a + 1, a + 1 + t, cmp);
double ans = 0;
for(int i = 1; i <= t; i++){
int fx = find(a[i].u), fy = find(a[i].v);
if(fx != fy){
p[fx] = fy;
ans += a[i].w;
}
}
printf("%.3lf\n", ans);
}
return 0;
}
4.POJ-2421 Constructing Roads
题意:给定一个 n 个点的完全图,求该图的最小生成树,其中有一些边必选。
其中最小生成树代表图中边权和最小的树。
那么根据题意就是把那些必选的边先连接出来,然后再做最小生成树
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
const int N = 10010;
const int inf = 0x3f3f3f3f;
int n, m, p[N];
int d[N][N];
struct Edge {
int a, b, w;
} edges[N];
bool cmp(Edge i, Edge j) {
return i.w < j.w;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
std::cin >> d[i][j];
int cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
edges[cnt].a = i, edges[cnt].b = j, edges[cnt++].w = d[i][j];
for (int i = 1; i <= n; i++) p[i] = i;
int q;
std::cin >> q;
while (q--) {
int a, b;
std::cin >> a >> b;
a = find(a), b = find(b);
if (a != b) p[a] = b;
}
std::sort(edges, edges + cnt, cmp);
int res = 0;
for (int i = 0; i < cnt; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a), b = find(b);
if (a != b) {
p[a] = b;
res += w;
}
}
std::cout << res << "\n";
return 0;
}
5.ZOJ-1586 QS Network
模板题
由于输入是邻接矩阵,所以直接选择prim算法来做
#include <bits/stdc++.h>
using namespace std;
const int N = 1001;
int n;
int g[N][N], dist[N], a[N];
bool vis[N];
int main(){
int tt;
scanf("%d", &tt);
while (tt--){
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];
}
}
memset(dist, 0x3f, sizeof dist);
memset(vis, 0, sizeof vis);
long long ans = 0;
for(int i = 0; i < n; i++){
int t = -1;
for(int j = 1; j <= n; j++)
if(!vis[j] && (t == -1 || dist[t] > dist[j]))
t = j;
vis[t] = true;
if(i) ans += dist[t];
for(int j = 1; j <= n; j++) dist[j] = min(dist[j], g[t][j]);
}
printf("%lld\n", ans);
}
return 0;
}
6.POJ-1789 Truck History
还是模板题,边的权值是字符串对应位置不同的个数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 2005;
struct node{
int u, v, w;
}a[N * N], temp;
int n, p[N];
char s[N][8];
int find(int x){
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
int get(int i, int j){
int ans = 0;
for(int k = 0; k < 7; k++)
if(s[i][k] != s[j][k])
ans ++;
return ans;
}
bool cmp(node i, node j){
return i.w < j.w;
}
int main(){
while(scanf("%d", &n), n){
for(int i = 1; i <= n; i++){
scanf("%s", s[i]);
p[i] = i;
}
int t = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
temp.u = i, temp.v = j, temp.w = get(i, j);
a[++t] = temp;
}
}
sort(a + 1, a + 1 + t, cmp);
int ans = 0;
for(int i = 1; i <= t; i++){
int x = a[i].u, y = a[i].v;
x = find(x), y = find(y);
if(x != y){
p[x] = y;
ans += a[i].w;
}
}
printf("The highest possible quality is 1/%d.\n", ans);
}
return 0;
}
7.POJ-2349 Arctic Network
题意转化即为选择k条边免费求最小生成树的第n-k大的边,这里选择Kruskal算法,因为在最小生成树往树中加的边都是通过排序的,所以只需要找出第n-k个加入树中的边即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 2005;
struct node{
int u, v;
double w;
}a[N * N], temp;
int n, m, p[N];
double x[N], y[N];
int find(int x){
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
bool cmp(node i, node j){
return i.w < j.w;
}
double dist(int i, int j){
double dx = x[i] - x[j], dy = y[i] - y[j];
return sqrt(dx * dx + dy * dy);
}
int main(){
int tt;
scanf("%d", &tt);
while(tt--){
scanf("%d%d", &m, &n);
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &x[i], &y[i]);
p[i] = i;
}
int t = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
temp.u = i, temp.v = j, temp.w = dist(i, j);
a[++t] = temp;
}
}
sort(a + 1, a + 1 + t, cmp);
double ans = 0;
int k = 0;
for(int i = 1; i <= t; i++){
if(k == n - m) break;
int fx = find(a[i].u), fy = find(a[i].v);
if(fx != fy){
p[fx] = fy;
ans = a[i].w;
k++;
}
}
printf("%.2lf\n", ans);
}
return 0;
}
8.POJ-1751 Highways
由于是稠密图,所以使用prim算法,这里也考察了对并查集连通操作的理解,也即对p数组的理解。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 1010, M = 2e6 + 7;
int n, m, x[N], y[N], p[N];
int dist[N], g[N][N];
bool vis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++){
cin >> x[i] >> y[i];
p[i] = 1, dist[i] = 0x3f3f3f3f, vis[i] = 0;
for(int j = 1; j <= i; j++){
g[i][j] = g[j][i] = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
}
}
cin >> m;
for(int i = 1; i <= m; i++){
int a, b;
cin >> a >> b;
g[a][b] = 0, g[b][a] = 0;
}
for(int i = 1; i <= n; i++) dist[i] = g[1][i];
vis[1] = 1, dist[1] = 0;
int res = 0;
for(int i = 1; i <= n; i++){
int min = 0x3f3f3f3f;
int t = 0;
for(int j = 1; j <= n; j++)
if(!vis[j] && dist[j] < min) min = dist[j], t = j;
vis[t] = 1;
if(g[p[t]][t] != 0) cout << t << " " << p[t] << "\n";
for(int j = 1; j <= n; j++)
if(!vis[j] && dist[j] > g[t][j]) dist[j] = g[t][j], p[j] = t;
}
return 0;
}
9.POJ-1258 Agri-Net
模板题
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 1010, M = 2e6 + 7;
int n, m, x[N], y[N], p[N];
int dist[N], g[N][N];
bool vis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (cin >> n){
for(int i = 1; i <= n; i++){
dist[i] = 0x3f3f3f3f, vis[i] = 0;
for(int j = 1; j <= n; j++){
cin >> g[i][j];
}
}
int res = 0;
for (int i = 0; i < n; i++){
int t = -1;
for (int j = 1; j <= n; j++)
if (!vis[j] && (t == -1 || dist[t] > dist[j]))
t = j;
vis[t] = 1;
if(i) res += dist[t];
for (int j = 1; j <= n; j++)
if (dist[j] > g[t][j])
dist[j] = g[t][j];
}
cout << res << "\n";
}
return 0;
}
10.POJ-3026 Borg Maze
bfs+最小生成树
用bfs暴力建图,再跑最小生成树。
注意这道题的nt出题人卡的输入,输入x,y后要用gets读一下空格,nt出题人
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
typedef pair<int, int> PII;
const int dx[] = {0, 0, -1, 1};
const int dy[] = {1, -1, 0, 0};
const int N = 200;
struct edge{
int u, v, w;
bool operator<(const edge &i) const{
return w < i.w;
}
} e[N * N], temp;
int n, m, cnt, tot, p[N];
char g[N][N];
char c[N];
int dist[N][N], num[N][N];
int find(int x){
if (x == p[x]) return x;
return p[x] = find(p[x]);
}
void bfs(int x, int y){
memset(dist, -1, sizeof dist);
queue<PII> q;
q.push(make_pair(x, y));
dist[x][y] = 0;
while (q.size()){
pair<int, int> t = q.front();
q.pop();
if (g[t.first][t.second] == 'S' || g[t.first][t.second] == 'A'){
e[++cnt] = (edge){num[x][y], num[t.first][t.second], dist[t.first][t.second]};
}
for (int i = 0; i < 4; i++){
int a = t.first + dx[i], b = t.second + dy[i];
if (a >= 0 && a < m && b < n || b >= 0 && g[a][b] != '#' && dist[a][b] == -1) {
dist[a][b] = dist[t.first][t.second] + 1;
q.push(make_pair(a, b));
}
}
}
}
int main(){
int tt;
scanf("%d", &tt);
while (tt--){
int n, m;
scanf("%d%d", &m, &n);
gets(g[0]);
tot = 0, cnt = 0;
for (int i = 0; i < n; i++){
gets(g[i]);
for (int j = 0; j < m; j++)
if (g[i][j] == 'A' || g[i][j] == 'S')
num[i][j] = ++tot;
}
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (g[i][j] == 'A' || g[i][j] == 'S')
bfs(i, j);
for (int i = 1; i <= tot; i++) p[i] = i;
sort(e + 1, e + 1 + cnt);
int ans = 0;
for (int i = 1; i <= cnt; i++){
int fx = find(e[i].u), fy = find(e[i].v);
if (fx != fy){
p[fx] = fy;
ans += e[i].w;
}
}
printf("%d\n", ans);
}
return 0;
}
11.POJ-1679 The Unique MST
次小生成树模板题
思路:先求出最小生成树,再枚举不在最小生成树上的边并加到树中,此时树上一定会形成环,那么删去其中最长的得到的便是次小生成树
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <vector>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <cmath>
using namespace std;
const int N = 105, M = 5050;
int n, m;
int p[N], dist[N][N];
vector<int> path[N];
struct node{
int a, b, w;
bool vis;
bool operator < (const node& i) const{
return w < i.w;
}
}e[M];
int find(int x){
if(p[x] == x) return x;
return p[x] = find(p[x]);
}
int main(){
int tt;
scanf("%d", &tt);
while(tt--){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
p[i] = i, path[i].clear(), path[i].push_back(i);
}
for(int i = 1; i <= m; i++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
e[i] = (node) {a, b, c};
}
sort(e + 1, e + 1 + m);
long long ans = 0;
int t = 1;
for(int i = 1; i <= m; i++){
int x = find(e[i].a), y = find(e[i].b);
if(x != y){
p[x] = y;
ans += e[i].w;
e[i].vis = 1;
t++;
int s1 = path[x].size(), s2 = path[y].size();
for(int j = 0; j < s1; j++)
for(int k = 0; k < s2; k++)
dist[path[x][j]][path[y][k]] = dist[path[y][k]][path[x][j]] = e[i].w;
for(int j = 0; j < s1; j++) path[y].push_back(path[x][j]);
if(t == n) break;
}
}
long long res = 0x3f3f3f3f;
for(int i = 1; i <= m; i++)
if(!e[i].vis)
res = min(res, ans + e[i].w - dist[e[i].a][e[i].b]);
if(res > ans) printf("%lld\n", ans);
else printf("Not Unique!\n");
}
return 0;
}
12.HDU-1233 还是畅通工程
模板题
#include <bits/stdc++.h>
using namespace std;
const int N = 100, M = N * (N - 1) >> 1;
int n, m, p[N];
struct node{
int u, v, w;
bool operator< (const node &i)const{
return w < i.w;
}
}e[M];
int find(int x){
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
while(cin >> n, n){
int m = n * (n - 1) >> 1;
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= m; i++){
int a, b, c;
cin >> a >> b >> c;
e[i] = {a, b, c};
}
sort(e + 1, e + 1 + m);
int ans = 0;
for(int i = 1; i <= m; i++){
int x = find(e[i].u), y = find(e[i].v);
if(x != y){
p[x] = y;
ans += e[i].w;
}
}
cout << ans << "\n";
}
return 0;
}
13.HDU-1875 畅通工程再续
模板题,权值不能小于10也不能超过1000
#include <bits/stdc++.h>
using namespace std;
const int N = 101, M = N * (N - 1) >> 1;
int n, m, p[N];
double x[N], y[N];
struct node{
int u, v;
double w;
bool operator< (const node &i)const{
return w < i.w;
}
}e[M];
int find(int x){
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
double dist(int i, int j){
double dx = x[i] - x[j], dy = y[i] - y[j];
return sqrt(dx * dx + dy * dy);
}
int main(){
int t;
scanf("%d", &t);
while(t --){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%lf%lf", &x[i], &y[i]), p[i] = i;
int tt = 0;
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
e[++tt] = {i, j, dist(i, j)};
}
}
sort(e + 1, e + 1 + tt);
double ans = 0;
int k = 0;
for(int i = 1; i <= tt; i++){
if(e[i].w < 10 || e[i].w > 1000) continue;
int x = find(e[i].u), y = find(e[i].v);
if(x != y){
p[x] = y;
ans += e[i].w;
k++;
}
}
if(k < n - 1) printf("oh!\n");
else printf("%.1lf\n", ans * 100);
}
return 0;
}