「题解」kuangbin 最小生成树

  • POJ-1251 Jungle Roads (水题,%c)
  • POJ-1287 Networking (水)
  • POJ-2031 Building a Space Station (%f浮点数尴尬精度,两球间距离)
  • POJ-2421 Constructing Roads (一些边已建好,简单处理一下)
  • ZOJ-1586 QS Network (处理一下边权)
  • HDU-1233 还是畅通工程 (水)
  • HDU-1875 畅通工程再续 (浮点数,条件连边)
  • HDU-1301 Jungle Roads (重复 -> POJ-1251)
  • POJ-2349 Arctic Network (第 K 大边)
  • POJ-1751 Highways (求最小生成树的边,加 0 权边)
  • UVA-10147 Highways (多组的 POJ-1751) +
  • POJ-1258 Agri-Net (水)
  • POJ-3026 Borg Maze (BFS + Kruskal,用最短路建图,垃圾输入)
  • POJ-1789 Truck History (Prim,Kruskal 超时)
  • POJ-1679 The Unique MST (次小生成树)

代码最后附简单题解

模板

Kruskal

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
 * Kruskal 算法求 MST */

#include <algorithm>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct  {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}  //排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}  //传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数

  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

Prim

12345678910111213141516171819202122232425262728293031323334
 * Prim 求 MST * 耗费矩阵 cost[][],标号从 0 开始,0~n-1 * 返回最小生成树的权值,返回 -1 表示原图不连通 */

const int INF = 0x3f3f3f3f;const int MAXN = 110;bool vis[MAXN];int lowc[MAXN];//点是 0 n-1int Prim(int cost[][MAXN], int n) {  int ans = 0;  memset(vis, false, sizeof(vis));  vis[0] = true;  for (int i = 1; i < n; i++) lowc[i] = cost[0][i];  for (int i = 1; i < n; i++) {    int minc = INF;    int p = -1;    for (int j = 0; j < n; j++)      if (!vis[j] && minc > lowc[j]) {        minc = lowc[j];        p = j;      }    if (minc == INF) return -1;  //原图不连通    ans += minc;    vis[p] = true;    for (int j = 0; j < n; j++) {      if (!vis[j] && lowc[j] > cost[p][j]) lowc[j] = cost[p][j];    }  }  return ans;}

SecondMST (Prim)

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
 * 次小生成树 * 求最小生成树时,用数组 Max[i][j] 来表示 MST 中 i 到 j 最大边权 * 求完后,直接枚举所有不在 MST 中的边,替换掉最大边权的边,更新答案 * 点的编号从 0 开始 */

#include <algorithm>#include <cmath>using namespace std;

const int MAXN = 110;const int INF = 0x3f3f3f3f;bool vis[MAXN];int lowc[MAXN];int pre[MAXN];int Max[MAXN][MAXN];// Max[i][j] 表示在最小生成树中从 i 到 j 的路径中的最大边权bool used[MAXN][MAXN];int Prim(int cost[][MAXN], int n) {  int ans = 0;  memset(vis, false, sizeof(vis));  memset(Max, 0, sizeof(Max));  memset(used, false, sizeof(used));  vis[0] = true;  pre[0] = -1;  for (int i = 1; i < n; i++) {    lowc[i] = cost[0][i];    pre[i] = 0;  }  lowc[0] = 0;  for (int i = 1; i < n; i++) {    int minc = INF;    int p = -1;

    for (int j = 0; j < n; j++)      if (!vis[j] && minc > lowc[j]) {        minc = lowc[j];        p = j;      }    if (minc == INF) return -1;    ans += minc;    vis[p] = true;    used[p][pre[p]] = used[pre[p]][p] = true;    for (int j = 0; j < n; j++) {      if (vis[j] && j != p)        Max[j][p] = Max[p][j] = max(Max[j][pre[p]], lowc[p]);      if (!vis[j] && lowc[j] > cost[p][j]) {        lowc[j] = cost[p][j];        pre[j] = p;      }    }  }  return ans;}

int smst(int cost[][MAXN], int n, int ans) {  int Min = INF;  for (int i = 0; i < n; i++)    for (int j = i + 1; j < n; j++)      if (cost[i][j] != INF && !used[i][j]) {        Min = min(Min, ans + cost[i][j] - Max[i][j]);      }  if (Min == INF) return -1;  //不存在  return Min;}

代码

POJ-1251 Jungle Roads

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
// POJ-1251 Jungle Roads// https://vjudge.net/problem/POJ-1251

#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct  {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int main() {  int n;  while (~scanf("%d", &n)) {    if (n == 0) break;    tol = 0;    for (int i = 0; i < n - 1; i++) {      char x;      int m;      scanf(" %c%d", &x, &m);      for (int j = 0; j < m; j++) {        char y;        int w;        scanf(" %c%d", &y, &w);        addedge(x - 'A', y - 'A', w);      }    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 裸题,scanf %c 读入尴尬// scanf(" %c%d", &x, &m);

POJ-1287 Networking

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
// POJ-1287 Networking// https://vjudge.net/problem/POJ-1287

#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct  {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int main() {  int n, m;  while (~scanf("%d", &n)) {    if (n == 0) break;    tol = 0;    scanf("%d", &m);    for (int i = 0; i < m; i++) {      int a, b, c;      scanf("%d%d%d", &a, &b, &c);      addedge(a, b, c);    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 裸题

POJ-2031 Building a Space Station

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
// POJ-2031 Building a Space Station// https://vjudge.net/problem/POJ-2031

#include <string.h>#include <algorithm>#include <cmath>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct  {  int u, v;  double w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, double w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1double Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  double ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    double w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

double len(double x, double y, double z, double xx, double yy, double zz) {  return sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y) + (zz - z) * (zz - z));}double cirlen(double x, double y, double z, double r, double xx, double yy,              double zz, double rr) {  double ll = len(x, y, z, xx, yy, zz);  if (ll - r - rr < 1e-5) {    return 0;  } else {    return ll - r - rr;  }}double xp[MAXN];double yp[MAXN];double zp[MAXN];double rp[MAXN];

int main() {  int n;  while (~scanf("%d", &n)) {    if (n == 0) break;    tol = 0;    for (int i = 0; i < n; i++) {      scanf("%lf%lf%lf%lf", &xp[i], &yp[i], &zp[i], &rp[i]);    }    for (int i = 0; i < n; i++) {      for (int j = i + 1; j < n; j++) {        addedge(i, j,                cirlen(xp[i], yp[i], zp[i], rp[i], xp[j], yp[j], zp[j], rp[j]));      }    }    double ans = Kruskal(n);    printf("%.3fn", ans);  }  return 0;}

// 浮点数的最小生成树,球之间的距离,距离小于零,就建一条权为 0 的边// POJ尴尬精度 %f

POJ-2421 Constructing Roads

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
// POJ-2421 Constructing Roads// https://vjudge.net/problem/POJ-2421

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct  {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}  //排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}  //传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数

  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int mapx[MAXN][MAXN];

int main() {  int N;  scanf("%d", &N);  for (int i = 1; i <= N; i++) {    for (int j = 1; j <= N; j++) {      scanf("%d", &mapx[i][j]);    }  }

  int Q;  scanf("%d", &Q);  for (int i = 0; i < Q; i++) {    int a, b;    scanf("%d%d", &a, &b);    mapx[a][b] = 0;    mapx[b][a] = 0;  }  for (int i = 1; i <= N; i++) {    for (int j = i + 1; j <= N; j++) {      if (i == j) continue;      addedge(i, j, min(mapx[i][j], mapx[j][i]));    }  }  int ans = Kruskal(N);  printf("%dn", ans);}

// 有一些路已建好,把边权改为 0 就好了

ZOJ-1586 QS Network

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
// ZOJ-1586 QS Network// https://vjudge.net/problem/ZOJ-1586

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 1100;     //最大点数const int MAXM = 2000000;  //最大边数int F[MAXN];               //并查集使用

int codec[MAXN];  // *&*

struct Edge {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;

      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}int main() {  int T;  scanf("%d", &T);  while (T--) {    tol = 0;    int n;    scanf("%d", &n);    for (int i = 1; i <= n; i++) {      scanf("%d", &codec[i]);    }    for (int i = 1; i <= n; i++) {      for (int j = 1; j <= n; j++) {        int w;        scanf("%d", &w);        if (i < j) addedge(i, j, w + codec[i] + codec[j]);      }    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 加边时,加一下适配器的价格

HDU-1233 还是畅通工程

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
// HDU-1233 还是畅通工程// https://vjudge.net/problem/HDU-1233

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct Edge {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}  //排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}  //传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数

  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}int main() {  int n;  while (~scanf("%d", &n)) {    if (n == 0) break;    tol = 0;    int m = n * (n - 1) / 2;    for (int i = 0; i < m; i++) {      int a, b, w;      scanf("%d%d%d", &a, &b, &w);      addedge(a, b, w);    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 裸题

HDU-1875 畅通工程再续

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
// HDU-1875 畅通工程再续// https://vjudge.net/problem/HDU-1875

#include <string.h>#include <algorithm>#include <cmath>#include <cstdio>using namespace std;

const int MAXN = 310;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct Edge {  int u, v;  double w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, double w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1double Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  double ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    double w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

double len(double x, double y, double xx, double yy) {  return sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));}

double xp[MAXN];double yp[MAXN];

int main() {  int T;  scanf("%d", &T);  while (T--) {    int n;    scanf("%d", &n);    if (n == 0) break;    tol = 0;    for (int i = 1; i <= n; i++) {      scanf("%lf%lf", &xp[i], &yp[i]);    }    for (int i = 1; i <= n; i++) {      for (int j = i + 1; j <= n; j++) {        double xly = len(xp[i], yp[i], xp[j], yp[j]);        if (xly >= 10 && xly <= 1000) addedge(i, j, xly * 100);      }    }    double ans = Kruskal(n);    if (ans > 0) {      printf("%.1fn", ans);

    } else {      printf("oh!n");    }  }  return 0;}

// 浮点数,条件连边

HDU-1301 Jungle Roads

123456 大专栏  「题解」kuangbin 最小生成树789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
// HDU-1301 Jungle Roads// https://vjudge.net/problem/HDU-1301

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct Edge {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int main() {  int n;  while (~scanf("%d", &n)) {    if (n == 0) break;    tol = 0;    for (int i = 0; i < n - 1; i++) {      char x;      int m;      scanf(" %c%d", &x, &m);      for (int j = 0; j < m; j++) {        char y;        int w;        scanf(" %c%d", &y, &w);        addedge(x - 'A', y - 'A', w);      }    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 竟然有重复的题,一模一样 -> POJ-1251// 裸题,scanf %c 读入尴尬// scanf(" %c%d", &x, &m);

POJ-2349 Arctic Network

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
// POJ-2349 Arctic Network// https://vjudge.net/problem/POJ-2349

#include <string.h>#include <algorithm>#include <cmath>#include <cstdio>#include <vector>using namespace std;

const int MAXN = 510;     //最大点数const int MAXM = 250000;  //最大边数int F[MAXN];              //并查集使用vector<double> vd;struct Edge {  int u, v;  double w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, double w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }

int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1double Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  double ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    double w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans = max(ans, w);      vd.push_back(w);      // ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

double len(double x, double y, double xx, double yy) {  return sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));}

double xp[MAXN];double yp[MAXN];

int main() {  int T;  scanf("%d", &T);  while (T--) {    vd.clear();    int n, s;    scanf("%d%d", &s, &n);    if (n == 0) break;    tol = 0;    for (int i = 1; i <= n; i++) {      scanf("%lf%lf", &xp[i], &yp[i]);    }    for (int i = 1; i <= n; i++) {      for (int j = i + 1; j <= n; j++) {        double xly = len(xp[i], yp[i], xp[j], yp[j]);        addedge(i, j, xly);      }    }    double ans = Kruskal(n);    ans = vd[vd.size() - s];    printf("%.2fn", ans);  }  return 0;}

// 最小生成树的第 K 大边

POJ-1751 Highways

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
// POJ-1751 Highways// https://vjudge.net/problem/POJ-1751

#include <string.h>#include <algorithm>#include <cmath>#include <cstdio>#include <vector>using namespace std;

const int MAXN = 1000;     //最大点数const int MAXM = 1000000;  //最大边数vector<int> vx;vector<int> vy;

int F[MAXN];  //并查集使用struct Edge {  int u, v;  double w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, double w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1double Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  double ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    double w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      if (w > 1e-5) {        vx.push_back(u);        vy.push_back(v);      }

      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

double len(double x, double y, double xx, double yy) {  return sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));}

double xp[MAXN];double yp[MAXN];

int main() {  int n;  scanf("%d", &n);  tol = 0;  for (int i = 1; i <= n; i++) {    scanf("%lf%lf", &xp[i], &yp[i]);  }  for (int i = 1; i <= n; i++) {    for (int j = i + 1; j <= n; j++) {      double xly = len(xp[i], yp[i], xp[j], yp[j]);      addedge(i, j, xly * 100);    }  }  int q;  scanf("%d", &q);  for (int i = 0; i < q; i++) {    int a, b;    scanf("%d%d", &a, &b);    addedge(a, b, 0);  }  double ans = Kruskal(n);

  for (int i = 0; i < vx.size(); i++) {    printf("%d %dn", vx[i], vy[i]);  }

  return 0;}

// 求最小生成树的边,加 0 权边

UVA-10147 Highways

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
// UVA-10147 Highways// https://vjudge.net/problem/UVA-10147

#include <string.h>#include <algorithm>#include <cmath>#include <cstdio>#include <vector>using namespace std;

const int MAXN = 1000;     //最大点数const int MAXM = 1000000;  //最大边数vector<int> vx;vector<int> vy;

int F[MAXN];  //并查集使用struct Edge {  int u, v;  double w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, double w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}//排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}//传入点数,返回最小生成树的权值,如果不连通返回 -1double Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数  double ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    double w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      if (w > 0) {        vx.push_back(u);        vy.push_back(v);      }

      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

double len(double x, double y, double xx, double yy) {  return sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));}

double xp[MAXN];double yp[MAXN];

int main() {  int T;  scanf("%d", &T);  while (T--) {    vx.clear();    vy.clear();

    int n;    scanf("%d", &n);    tol = 0;    for (int i = 1; i <= n; i++) {      scanf("%lf%lf", &xp[i], &yp[i]);    }    for (int i = 1; i <= n; i++) {      for (int j = i + 1; j <= n; j++) {        double xly = len(xp[i], yp[i], xp[j], yp[j]);        addedge(i, j, xly * 100);      }    }    int q;    scanf("%d", &q);    for (int i = 0; i < q; i++) {      int a, b;      scanf("%d%d", &a, &b);      addedge(a, b, 0);    }    double ans = Kruskal(n);

    for (int i = 0; i < vx.size(); i++) {      printf("%d %dn", vx[i], vy[i]);    }    if (vx.size() == 0) printf("No new highways needn");    if (T != 0) printf("n");  }

  return 0;}

// 求最小生成树的边,加 0 权边

POJ-1258 Agri-Net

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
// POJ-1258 Agri-Net// https://vjudge.net/problem/POJ-1258

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 110;    //最大点数const int MAXM = 10000;  //最大边数int F[MAXN];             //并查集使用struct Edge {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}  //排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}  //传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数

  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int main() {  int n;  while (~scanf("%d", &n)) {    tol = 0;    for (int i = 1; i <= n; i++) {      for (int j = 1; j <= n; j++) {        int w;        scanf("%d", &w);        addedge(i, j, w);      }    }    int ans = Kruskal(n);    printf("%dn", ans);  }  return 0;}

// 裸题

POJ-3026 Borg Maze

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
// POJ-3026 Borg Maze// https://vjudge.net/problem/POJ-3026

#include <string.h>#include <algorithm>#include <cstdio>#include <queue>using namespace std;

const int MAXN = 1100;     //最大点数const int MAXM = 1000000;  //最大边数int F[MAXN];               //并查集使用struct Edge {  int u, v, w;} edge[MAXM];  //存储边的信息,包括起点/终点/权值int tol;       //边数,加边前赋值为 0void addedge(int u, int v, int w) {  edge[tol].u = u;  edge[tol].v = v;  edge[tol++].w = w;}  //排序函数,讲边按照权值从小到大排序bool cmp(Edge a, Edge b) { return a.w < b.w; }int find(int x) {  if (F[x] == -1)    return x;  else    return F[x] = find(F[x]);}  //传入点数,返回最小生成树的权值,如果不连通返回 -1int Kruskal(int n) {  memset(F, -1, sizeof(F));  sort(edge, edge + tol, cmp);  int cnt = 0;  //计算加入的边数

  int ans = 0;  for (int i = 0; i < tol; i++) {    int u = edge[i].u;    int v = edge[i].v;    int w = edge[i].w;    int t1 = find(u);    int t2 = find(v);    if (t1 != t2) {      ans += w;      F[t1] = t2;      cnt++;    }    if (cnt == n - 1) break;  }  if (cnt < n - 1)    return -1;  //不连通  else    return ans;}

int mapx[MAXN][MAXN];

int dirx[10] = {0, 1, 0, -1};int diry[10] = {1, 0, -1, 0};

int n, m;void bfs(int x, int y) {  bool vis[MAXN][MAXN];  memset(vis, false, sizeof(vis));  queue<pair<pair<int, int>, int> > Q;  Q.push(make_pair(make_pair(x, y), 0));  vis[x][y] = true;  while (!Q.empty()) {    int xt = Q.front().first.first;    int yt = Q.front().first.second;    int times = Q.front().second;    Q.pop();    if (mapx[x][y] != mapx[xt][yt] && mapx[xt][yt] != 0) {      addedge(mapx[x][y], mapx[xt][yt], times);    }    for (int i = 0; i < 4; i++) {      int tx = xt + dirx[i];      int ty = yt + diry[i];

      if (tx >= 0 && ty >= 0 && tx < n && ty < m && !vis[tx][ty]) {        if (mapx[tx][ty] != -1) {          Q.push(make_pair(make_pair(tx, ty), times + 1));          vis[tx][ty] = true;        }      }    }  }}

int main() {  int T;  scanf("%d", &T);  char str[100];  while (T--) {    tol = 0;    memset(mapx, -1, sizeof(mapx));    int cnt = 0;    scanf("%d%d", &n, &m);    gets(str);    for (int i = 0; i < m; i++) {      gets(str);      for (int j = 0; j < n; j++) {        if (str[j] == 'A' || str[j] == 'S') {          mapx[i][j] = ++cnt;        } else if (str[j] == ' ') {          mapx[i][j] = 0;        }      }    }    for (int i = 0; i < m; i++) {      for (int j = 0; j < n; j++) {        if (mapx[i][j] > 0) {          bfs(i, j);        }      }    }    int ans = Kruskal(cnt);    printf("%dn", ans);  }  return 0;}

// BFS + Kruskal// 用无权图最短路权值加边// 一开始 MAXN 和 MAXM 开小了,wa...wa...wa...

POJ-1789 Truck History

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
// POJ-1789 Truck History// https://vjudge.net/problem/POJ-1789

#include <string.h>#include <algorithm>#include <cstdio>using namespace std;

const int MAXN = 2010;  //最大点数const int INF = 0x3f3f3f3f;bool vis[MAXN];int lowc[MAXN];int cost[MAXN][MAXN];//点是 0 n-1int Prim(int cost[][MAXN], int n) {  int ans = 0;  memset(vis, false, sizeof(vis));  vis[0] = true;  for (int i = 1; i < n; i++) lowc[i] = cost[0][i];  for (int i = 1; i < n; i++) {    int minc = INF;    int p = -1;    for (int j = 0; j < n; j++)      if (!vis[j] && minc > lowc[j]) {        minc = lowc[j];        p = j;      }    if (minc == INF) return -1;  //原图不连通    ans += minc;    vis[p] = true;    for (int j = 0; j < n; j++) {      if (!vis[j] && lowc[j] > cost[p][j]) lowc[j] = cost[p][j];    }  }  return ans;}char str[MAXN][10];int len(int a, int b) {  int ans = 0;  for (int i = 0; i < 7; i++) {    if (str[a][i] != str[b][i]) {      ans++;    }  }  return ans;}

int main() {  int n;  while (~scanf("%d", &n)) {    if (n == 0) break;    for (int i = 0; i < n; i++) {      scanf("%s", str[i]);    }    for (int i = 0; i < n; i++) {      for (int j = 0; j < n; j++) {        if (i == j) continue;        cost[i][j] = len(i, j);      }    }    int ans = Prim(cost, n);    printf("The highest possible quality is 1/%d.n", ans);  }  return 0;}

// Kruskal 超时,用 Prim !

POJ-1679 The Unique MST

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
// POJ-1679 The Unique MST// https://vjudge.net/problem/POJ-1679

#include <string.h>#include <algorithm>#include <cstdio>#include <cmath>using namespace std;

const int MAXN = 110;const int INF = 0x3f3f3f3f;bool vis[MAXN];int lowc[MAXN];int pre[MAXN];int Max[MAXN][MAXN];// Max[i][j] 表示在最小生成树中从 i 到 j 的路径中的最大边权bool used[MAXN][MAXN];int Prim(int cost[][MAXN], int n) {  int ans = 0;  memset(vis, false, sizeof(vis));  memset(Max, 0, sizeof(Max));  memset(used, false, sizeof(used));  vis[0] = true;  pre[0] = -1;  for (int i = 1; i < n; i++) {    lowc[i] = cost[0][i];    pre[i] = 0;  }  lowc[0] = 0;  for (int i = 1; i < n; i++) {    int minc = INF;    int p = -1;

    for (int j = 0; j < n; j++)      if (!vis[j] && minc > lowc[j]) {        minc = lowc[j];        p = j;      }    if (minc == INF) return -1;    ans += minc;    vis[p] = true;    used[p][pre[p]] = used[pre[p]][p] = true;    for (int j = 0; j < n; j++) {      if (vis[j] && j != p)        Max[j][p] = Max[p][j] = max(Max[j][pre[p]], lowc[p]);      if (!vis[j] && lowc[j] > cost[p][j]) {        lowc[j] = cost[p][j];        pre[j] = p;      }    }  }  return ans;}

int smst(int cost[][MAXN],int n,int ans){    int Min=INF;    for(int i=0;i<n;i++)        for(int j=i+1;j<n;j++)            if(cost[i][j]!=INF && !used[i][j])            {                Min=min(Min,ans+cost[i][j]-Max[i][j]);            }    if(Min==INF)return -1;//不存在    return Min;}

int costx[MAXN][MAXN];

int main() {  int T;  scanf("%d", &T);  while (T--) {    memset(costx, 0x3f, sizeof(costx));    int n, m;    scanf("%d%d", &n, &m);    int sum = 0;    for (int i = 0; i < n; i++) costx[i][i] = 0;    for (int i = 0; i < m; i++) {      int a, b, c;      scanf("%d%d%d", &a, &b, &c);      a--;      b--;      costx[a][b] = c;      costx[b][a] = c;    }    int ans = Prim(costx, n);    int ansMST = smst(costx, n,ans);

    if (ans == ansMST) {      printf("Not Unique!n");    } else {      printf("%dn", ans);    }  }  return 0;}

// 次小生成树,不知道为什么最小生成树计数不行// kuangbin 题解:https://www.cnblogs.com/kuangbin/p/3147329.html

原文地址:https://www.cnblogs.com/wangziqiang123/p/11718014.html

时间: 2024-09-30 20:09:59

「题解」kuangbin 最小生成树的相关文章

「题解」「美团 CodeM 资格赛」跳格子

目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞定,最后无奈 \(90pts\) . 然而 \(T2\) 想到很多很奇怪的做法,结果正解在 \(28min\) 之内做出... 结果 \(T3\) 是本人最不擅长的伪期望,直接跳过,啥都没得. 来水一发 \(T1\) 的题解... 题目描述 点这里 考场思路 其实并没有什么十分特别的思路,就是一通乱

「题解」:[loj2763][JOI2013]现代豪宅

问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东西方向$M$列,南北方向$N$行的正方形房间组成. 从西面开始第$x$列,从南面开始第y行的房间用$(x,y)$表示. 相邻的两个房间之间都有一扇门.对于每扇门,门关上表示不可通行,门打开表示可以通行. 当门打开时,从门一边的房间走到另一边的房间需要$1$分钟. 另外,一些房间中有一个开关,如果连续

「题解」:世界线

问题 A: 世界线 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 我刚学完bitset就考了???然而我赛时并没有想到是bitset…… 话说我bitset还是没有颓完啊赶紧去补一波时空复杂度…… bitset优化暴力(据某诺神说这是bitset果题??) 注意dfs的时侯如果目标节点已经搜过了直接用当前节点的bitset或上目标节点的bitset值即可. 还有$6e4×6e4$会炸空间.开一个$6e4×3e2$的然后一半一半跑即可. 至于怎么跑一半,直接判断和

「题解」:联

问题 A: 联 时间限制: 2 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 解法1:离散化+线段树. 1e18的数据范围直接离散化掉所有的l和r,加一个映射数组表示间距即可. 线段树维护区间和,扫0的时候判定子树和等不等于子树大小. 维护两个标记:lazy(将一个区间赋值成什么).xr(是否异或整个区间)跑就完了. (我会说我沉迷解法二而解法一是水的么 感谢Larry提供的优质讲解及代码) #include<bits/stdc++.h> #define int long l

「题解」:X国的军队

问题 A: X国的军队 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 简单贪心. 按照存活的士兵数量(即参加战斗的士兵数量减去阵亡的士兵数量)排序. 若存活士兵数量相同则按照参与战斗的士兵数量排序. 顺序扫一遍统计答案. #include<bits/stdc++.h> #define int long long #define rint register int #define read(A) A=init() using namespace std; inl

「题解」:Simple

问题 A: Simple 时间限制: 1 Sec  内存限制: 256 MB 题面 题面谢绝公开. 题解 不算数学的数学题?? 直接枚举会重.$60%$两种算法:1.无脑$vis$数组记录.2.$exgcd$解方程判定是否有解. $100%$:首先考虑特殊情况:$n$.$m$互质. 我们设$n*x+m*y=z$,考虑枚举$y$和$x$,不难发现,当$y>=x$的时候均能找到一个$y'$使得$n|(y-y')$. 于是会出现重复.因此只需枚举$y([0,n-1])$,计算贡献即可. 对于一般情况,

「题解」:e

问题 B: e 时间限制: 2 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 话说一天考两个主席树这回事…… 正解可以叫树上主席树??(脸哥说也叫主席树上树???) 对于树上的每一条链建主席树,支持链上查询前驱和后继. 对于所有的$p[i]$,他说怎么得到就按他说的做就好,然后求所有$p[i]$的$LCA$. 对于每个$p[i]$到$LCA$的链上查一次$r$的前驱和后继更新答案即可. 注意:参数不要传反.别一个特判把自己判掉.pre和nxt的代码不要粘贴,粘贴了不要忘记改掉内

「题解」:毛一琛/$cow$ $subsets$

问题 A: 毛一琛/$cow$ $subsets$ 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 题名貌似是个大神??看起来像是签到题然后就死了. 首先$O(3^n)$算法显然.也显然过不去$20$的测试点. 正解是赫赫有名的$meet$ $in$ $the$ $middle$算法.数据在40以内的都能用$meet$ $in$ $the$ $middle$?? 对于两半路径,可以拼起来并且构成合法答案的条件是两人获得的分数相同. 所以一个比较聪明的办法是,不去记

「题解」:毛三琛

问题 C: 毛三琛subset 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 一眼题解随机化,吓够呛.一句话题解:二分答案加剪枝. 外层枚举$x$,然后二分答案暴力$check$.如果当前答案对于x的check失败就continue, 因为在当前的x中不可能找到比当前答案更优秀的解.加clock卡常可以A. 貌似不需要看脸.毕竟我这个非洲人都一遍A了.复杂度$O(np+nlognlogp)$. 代码:(ps.$¥$神指出了我代码的缺陷:其实在外面直接赋值可以少一