复习 [kuangbin带你飞]专题6 最小生成树

目录

  • 1. poj 1251 Jungle Roads
  • 2. poj 1287 Networking
  • 3. poj 2301 Building a Space Station
  • 4. poj 2421 Constructing Roads
  • 5. zoj 1610 QS Network
  • 6. poj 1789 Truck History
  • 7. poj 2349 Arctic Network
  • 8. poj 1751 Highways
  • 9. poj 1258 Agri-Net
  • 10. poj 3026 Borg Maze
  • 11. poj 1679 The Unique MST

1. poj 1251 Jungle Roads

  • 不知道是什么题,一看之前已经做过了,应该很简单吧
#include 
#include 
using namespace std;
const int MAXN = 200;
int set[MAXN];
struct Edge{
	int x,y,z;
}edge[MAXN];
int Find_set(int x){
	if(x != set[x]) set[x] = Find_set(set[x]);
	return set[x];
}
void Kruskal(int num,int m){
	int ans = 0;
	int tot = 0;
	for(int i=0;i<num;i++){
		set[i] = i;
	}
	for(int i=0;i<m;i++){
		int x = Find_set(edge[i].x);
		int y = Find_set(edge[i].y);
		if(x == y) continue;
		set[x] = set[y];
		tot++;
		ans += edge[i].z;
		if(tot == num-1) break;
	}
	cout<<ans<<endl;
}

bool cmp(Edge x,Edge y){
	return x.z<y.z;
}
int main(){
	int n,m;
	char c,cc;
	while(cin>>n&&n){
		int num = 0;
		for(int i=1;i<n;i++){
			cin>>c>>m;
			for(int j=0;j<m;j++){
				edge[num].x = c-'A';
				cin>>cc;
				edge[num].y = cc-'A';
				cin>>edge[num].z;	
				num++;
			}
		}
		sort(edge,edge+num,cmp);
		Kruskal(n,num);
	}
	return 0;
} 

2. poj 1287 Networking

  • 水题
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int s[MAXN];
struct Edge{
    int u, v, w;
    bool operator < (const Edge &B)const{
        return w < B.w;
    }
}edge[MAXN];
int FIND(int x){
    return x == s[x] ? x : s[x] = FIND(s[x]);
}
int Kruskal(int n, int m){
    int ans = 0;
    int num = 0;
    for(int i=0;i<m;i++){
        int u = edge[i].u;
        int v = edge[i].v;
        u = FIND(u);
        v = FIND(v);
        if(u == v) continue;
        s[u] = v;
        num += 1;
        ans += edge[i].w;
        if(num == n - 1) break;
    }
    return ans;
}
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    while(cin >> n){
        if(n == 0) break;
        for(int i=1;i<=n;i++) s[i] = i;
        cin >> m;
        for(int i=0;i<m;i++){
            cin >> edge[i].u >> edge[i].v >> edge[i].w;
        }sort(edge, edge + m);
        cout << Kruskal(n, m) << '\n';
    }
    return 0;
}

3. poj 2301 Building a Space Station

  • 三维最小生成树,水题
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int MAXN = 300;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
struct Point{
    double x, y, z;
    double r;
}pt[MAXN];
int dcmp(double x){
    if(fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
struct Line{
    int x, y;
    double dis;
    bool operator < (const Line &B)const{
        return dcmp(dis - B.dis) < 0;
    }
}le[30000];
int s[MAXN];
int FIND(int x){
    return x == s[x] ? x : s[x] = FIND(s[x]);
}
double kruskal(int n, int m){
    double ans = 0;
    int num = 0;
    for(int i=0;i<m;i++){
        int x = FIND(le[i].x);
        int y = FIND(le[i].y);
        if(x == y) continue;
        num += 1;
        s[x] = y;
        ans += le[i].dis;
        if(num == n - 1) break;
    }
    return ans;
}
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    while(cin >> n && n){
        for(int i=1;i<=n;i++) s[i] = i;
        for(int i=1;i<=n;i++){
            cin >> pt[i].x >> pt[i].y >> pt[i].z >> pt[i].r;
        }
        int m = 0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                le[m].dis = sqrt(pow(pt[i].x - pt[j].x, 2) + pow(pt[i].y - pt[j].y, 2) + pow(pt[i].z - pt[j].z, 2));    
                le[m].dis = max(0.0, le[m].dis - pt[i].r - pt[j].r);
                le[m].x = i;
                le[m].y = j;
                m += 1;
            }
        }sort(le, le + m);
        for(int i=0;i<m;i++){
            // cout << le[i].x << ' ' << le[i].y << ' ' << le[i].dis << '\n';
        }
        cout << fixed << setprecision(3) << kruskal(n, m) << '\n';
    }
    return 0;
}

4. poj 2421 Constructing Roads

  • 给定一些树上路径,让补全最小生成树,水题
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int MAXN = 111;
const int INF = 0x3f3f3f3f;
struct st{
    int x, y;
    int dis;
    bool operator <(const st &B)const{
        return dis < B.dis;
    }
}pt[MAXN * MAXN];
int s[MAXN];
int FIND(int x){
    return x == s[x] ? x : s[x] = FIND(s[x]);
}
int num = 0;
int kruskal(int n, int m){
    int ans = 0;
    if(num >= n - 1) return 0;
    for(int i=0;i<m;i++){
        int x = FIND(pt[i].x);
        int y = FIND(pt[i].y);
        if(x == y) continue;
        s[x] = y;
        ans += pt[i].dis;
        num += 1;
        if(num == n - 1) break;
    }
    return ans;
}
int main(){
    #ifdef LOCAL
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
    #endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    int q;
    cin >> n;
    for(int i=1;i<=n;i++) s[i] = i;
    int m = 0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin >> pt[m].dis;
            if(i == j) continue;
            pt[m].x = i;
            pt[m].y = j;
            m += 1;
        }
    }sort(pt, pt + m);
    cin >> q;
    while(q--){
        int u, v;
        cin >> u >> v;
        u = FIND(u);
        v = FIND(v);
        if(u == v) continue;
        num += 1;
        s[u] = v;
    }
    cout << kruskal(n, m);
    return 0;
}

5. zoj 1610 QS Network

  • 给出点权边权,求最小生成树,水题
#include 

using namespace std;

struct st{
    int x, y;
    int dis;
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        vector<int> s(n);
        for(int i=0;i<n;i++) s[i] = i;
        vector<int> a(n);
        for(int i=0;i<n;i++) cin >> a[i];
        function<int(int)> FIND = [&](int x){
            return x == s[x] ? x : s[x] = FIND(s[x]);
        };
        vector<st> vs;        
        function<int()> kruskal = [&](){
            int num = 0;
            int ans = 0;
            for(int i=0;i<vs.size();i++){
                int x = FIND(vs[i].x);
                int y = FIND(vs[i].y);
                if(x == y) continue;
                s[x] = y;

                ans += vs[i].dis;
                if(num == n - 1) break;
            }
            return ans;
        };
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                int x;
                cin >> x;
                if(i <= j) continue;
                vs.push_back(st{i, j, x + a[i] + a[j]});
            }
        }
        sort(vs.begin(), vs.end(), [&](st x, st y){
            return x.dis < y.dis;
        });
        cout << kruskal() << '\n';
    }
    return 0;
}

6. poj 1789 Truck History

  • 题意仔细理解一下,将每个字符串看作一个点,实际上是求这个图的最小生成树的权值和
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int N = 2e3 + 10;

struct st{
  int u, v, w;
  bool operator < (const st &B)const{
    return w < B.w;
  }
}s[N * N];
string Data[N];
int tot = 0;
int a = 0;
int b = 0;
int st[N];
int FIND(int x){
  return x == st[x] ? x : st[x] = FIND(st[x]);
}
void Kruskal(int n, int m){
  int num = 0;
  for(int i=0;i<m;i++){
    int u = FIND(s[i].u);
    int v = FIND(s[i].v);
    int w = s[i].w;
    if(u == v) continue;
    st[u] = v;
    a += w;
    num += 1;
    if(num == n - 1) break;
  }
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  while(cin >> n && n){
    for(int i=0;i<n;i++){
      cin >> Data[i];
      st[i] = i;
    }
    tot = a = b = 0;
    for(int i=0;i<n;i++){
      for(int j=i+1;j<n;j++){
        int sz = (int)Data[i].size();
        int w = 0;
        for(int k=0;k<sz;k++){
          if(Data[i][k] != Data[j][k]) w += 1;
        }
        s[tot].u = i;
        s[tot].v = j;
        s[tot].w = w;
        b += w;
        tot += 1;
      }
    }sort(s, s + tot);
    Kruskal(n, tot);
    cout << "The highest possible quality is " << 1 << '/' << a << ".\n";
  }
  return 0;
}

7. poj 2349 Arctic Network

  • 之前做过了,应该也是个水题
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int N = 600;
typedef double DB;
const DB eps = 1e-8;
int cmp(double x){
  if(fabs(x) < eps) return 0;
  return x < 0 ? -1 : 1;
}
struct st{
  int x, y;
  double z;
  bool operator < (const st &B)const{
    return cmp(z - B.z) < 0;
  }
}Data[N * N];
int ss[N];
int FIND(int x){
  return ss[x] == x ? x :ss[x] = FIND(ss[x]);
}
vector<double> vec;
void Kruskal(int n, int m){
  int num = 0;
  for(int i=0;i<m;i++){
    int u = Data[i].x;
    int v = Data[i].y;
    double w = Data[i].z;
    u = FIND(u);
    v = FIND(v);
    if(u == v) continue;
    ss[u] = v;
    num += 1;
    vec.push_back(w);
    if(num == n - 1) break;
  }
}
pair<double, double> vs[N];
int main(){
  int tt;
  cin >> tt;
  while(tt--){
    int s, p;
    cin >> s >> p;
    for(int i=1;i<=p;i++) ss[i] = i;
    int m = 0;
    for(int i=1;i<=p;i++){
      cin >> vs[i].first >> vs[i].second;
    }
    for(int i=1;i<=p;i++){
      for(int j=i+1;j<=p;j++){
        Data[m].x = i;
        Data[m].y = j;
        Data[m].z = hypot(vs[i].first - vs[j].first, vs[i].second - vs[j].second);
        m += 1;
      }
    }sort(Data, Data + m);
    vec.clear();
    Kruskal(p, m);
    reverse(vec.begin(), vec.end());
    cout << fixed << setprecision(2) << *(vec.begin() + s - 1) << '\n';
  }
  return 0;
}

8. poj 1751 Highways

  • 给你图中几条路径,补充完整最小生成树,已经构成路径的点放到一个集合里面即可,然后正常求
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int N = 760;
int x[N], y[N];
struct st{
  int u, v;
  ll w;
  bool operator < (const st &B)const{
    return w < B.w;
  }
}s[N * N];
int st[N];
int FIND(int x){
  return x == st[x] ? x : st[x] = FIND(st[x]);
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  int tot = 0;
  for(int i=0;i<n;i++){
    cin >> x[i] >> y[i];
    st[i] = i;
  }
  for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
      s[tot].u = i;
      s[tot].v = j;
      s[tot].w = 1ll * (x[j] - x[i]) * (x[j] - x[i]) + 1ll * (y[j] - y[i]) * (y[j] - y[i]);
      tot += 1;
    }
  }sort(s, s + tot);
  int m;
  cin >> m;
  for(int i=0;i<m;i++){
    int u, v;
    cin >> u >> v;
    u -= 1;
    v -= 1;
    u = FIND(u);
    v = FIND(v);
    if(u != v) st[u] = v;
  }
  vector<pair<int, int> > vs;
  for(int i=0;i<tot;i++){
    int u = FIND(s[i].u);
    int v = FIND(s[i].v);
    int w = s[i].w;
    if(u == v) continue;
    st[u] = v;
    vs.push_back(make_pair(s[i].u + 1, s[i].v + 1));
  }
  for(int i=0;i<(int)vs.size();i++){
    cout << vs[i].first << ' ' << vs[i].second << '\n';
  }
  return 0;
}

9. poj 1258 Agri-Net

  • 啊这,注意多测
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
const int N = 200;
struct st{
  int u, v;
  int w;
  bool operator < (const st &B)const{
    return w < B.w;
  }
}s[N * N];
int st[N];
int FIND(int x){
  return x == st[x] ? x : st[x] = FIND(st[x]);
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  while(cin >> n){
    int tot = 0;
    for(int i=0;i<n;i++){
      st[i] = i;
      for(int j=0;j<n;j++){
        int x;
        cin >> x;
        if(i >= j) continue;
        s[tot].u = i;
        s[tot].v = j;
        s[tot].w = x;
        tot += 1;
      }
    }
    sort(s, s + tot);
    int ans = 0;
    for(int i=0;i<tot;i++){
      int u = FIND(s[i].u);
      int v = FIND(s[i].v);
      int w = s[i].w;
      if(u == v) continue;
      st[u] = v;
      ans += w;
    }
    cout << ans << '\n';
  }
  return 0;
}

10. poj 3026 Borg Maze

  • 这个题是个好题,说你在位置 S S S,现在要去每一个 A A A点,然后你走的过程中可以分裂,换句话说就是你不用走回头路,然后问你最少需要多长时间完成这个任务
  • 真好久都没有遇到过这样的读入了,我不理解为什么 g e t c h a r ( ) getchar() getchar()读回车不对,涨姿势了,可以用sacnf("%d%d\n")读这个回车,然后这样读就能过,但是我用scanf("%d%d);getchar();就不行,也许是他输入没那么规范?
  • 说说这个题的思路,因为你走路的过程中可以分裂,所以容易想到可以转化为最小生成树问题,也就是把每两个点的最短距离都求出来,然后建一棵MST就好了,边权和即为答案
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;

const int N = 105;
char mp[N][N];
int Data[N][N];
bool vis[N][N];
int n, m;

int now;
struct st{
  int x, y, d;
  st(){}
  st(int x, int y, int d): x(x), y(y), d(d){}
};
int xx[] = {1, 0, -1, 0};
int yy[] = {0, 1, 0, -1};
struct Some{
  int u, v, w;
  bool operator < (const Some &B)const{
    return w < B.w;
  }
}some[205 * 105];
int s[105];
int FIND(int x){
  return x == s[x] ? x : s[x] = FIND(s[x]);
}
int main(){
  int t;
  scanf("%d", &t);
  while(t--){
    scanf("%d%d\n", &m, &n);
    // 这个地方用getchar()就是错的???
    memset(Data, 0, sizeof Data);
    now = 1;
    for(int i=1;i<=n;i++){
      for(int j=1;j<=m;j++){
        scanf("%c", &mp[i][j]);
        if(mp[i][j] == 'A' || mp[i][j] == 'S'){
          Data[i][j] = now;
          now += 1;
        }
      }getchar();
    }
    for(int i=1;i<=now;i++){
      s[i] = i;
    }
    int kk = 0;
    for(int i=1;i<=n;i++){
      for(int j=1;j<=m;j++){
        if(mp[i][j] == 'A' || mp[i][j] == 'S'){
          memset(vis, 0, sizeof vis);
          st s = st(i, j, 0);
          queue<st> q;
          q.push(s);
          while(!q.empty()){
            s = q.front();
            q.pop();
            vis[s.x][s.y] = true;
            for(int k=0;k<4;k++){
              int dx = s.x + xx[k];
              int dy = s.y + yy[k];
              if(dx < 1 || dy < 1 || dx > n || dy > m || mp[dx][dy] == '#' || vis[dx][dy]) continue;
              if(mp[dx][dy] == 'A' || mp[dx][dy] == 'S'){
                some[kk].u = Data[dx][dy];
                some[kk].v = Data[i][j];
                some[kk].w = s.d + 1;
                kk += 1;                
              }
              vis[dx][dy] = true;
              q.push(st(dx, dy, s.d + 1));
            }
          }
        }
      }
    }sort(some, some + kk);
    int ans = 0;
    for(int i=0;i<kk;i++){
      int u = FIND(some[i].u);
      int v = FIND(some[i].v);
      int w = some[i].w;
      if(u == v) continue;
      s[u] = v;
      ans += w;
    }
    printf("%d\n", ans);
  }
  return 0;
}

11. poj 1679 The Unique MST

  • 问最小生成树是否唯一
  • 暴力方法显然,也可以通过,就是求出最小生成树以后逐一禁用每一条边,然后再求,每次求完之后看得到的最小生成树的边权和是不是唯一的,容易写出下面的程序
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int N = 200;

int st[N], s_u[N], s_v[N];
int FIND(int x){
  return x == st[x] ? x : st[x] = FIND(st[x]);
}
struct st{
  int u, v, w;
  bool operator < (const st &B)const{
    return w < B.w;
  }
}s[N * N];
int main(){
  int t;
  cin >> t;
  while(t--){
    int n, m;
    cin >> n >> m;
    for(int i=1;i<=n;i++) st[i] = i;
    for(int i=0;i<m;i++){
      cin >> s[i].u >> s[i].v >> s[i].w;
    }sort(s, s + m);
    int ans = 0;
    bool ok = true;
    int now = 0;
    for(int i=0;i<m;i++){
      int u = FIND(s[i].u);
      int v = FIND(s[i].v);
      int w = s[i].w;
      if(u == v) continue;
      s_u[now] = s[i].u;
      s_v[now] = s[i].v;
      now += 1;
      st[u] = v;
      ans += w;
    }
    for(int i=0;i<now;i++){
      for(int j=1;j<=n;j++) st[j] = j;
      int res = 0;
      int k = 0;
      for(int j=0;j<m;j++){
        int u = FIND(s[j].u);
        int v = FIND(s[j].v);
        int w = s[j].w;
        if(u == v || (s_u[i] == s[j].u && s_v[i] == s[j].v)) continue;
        res += w;
        st[u] = v;
        k += 1;
      }
      if(res == ans && k == n - 1) ok = false;
    }
    if(ok) cout << ans << '\n';
    else{
      cout << "Not Unique!" << '\n';
    }
  }
  return 0;
}
  • 如果按照常规想法,就是求这棵树的次小生成树,如果发现它也是最小生成树,那么就说明MST不唯一,基本想法就是把除了MST以外的最短边加入到MST中,这样一定出现了一个环,比如说我们加入的是 u − > v u->v u>v这条边,设权值为 w w w,如果发现 u − > l c a u->lca u>lca或者 v − > l c a v->lca v>lca这二者之间存在一条边边权等于 w w w,那么说明次小生成树不唯一

你可能感兴趣的:(专项训练,c++,算法,动态规划)