一、AcWing 1140. 最短网络
【题目描述】
 农夫约翰被选为他们镇的镇长!
 他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。
 约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。
 约翰的农场的编号是
    
     
      
       
        1
       
      
      
       1
      
     
    1,其他农场的编号是
    
     
      
       
        2
       
       
        ∼
       
       
        n
       
      
      
       2∼n
      
     
    2∼n。
 为了使花费最少,他希望用于连接所有的农场的光纤总长度尽可能短。
 你将得到一份各农场之间连接距离的列表,你必须找出能连接所有农场并使所用光纤最短的方案。
【输入格式】
 第一行包含一个整数
    
     
      
       
        n
       
      
      
       n
      
     
    n,表示农场个数。
 接下来
    
     
      
       
        n
       
      
      
       n
      
     
    n行,每行包含
    
     
      
       
        n
       
      
      
       n
      
     
    n个整数,输入一个对角线上全是
    
     
      
       
        0
       
      
      
       0
      
     
    0的对称矩阵。
 其中第
    
     
      
       
        x
       
       
        +
       
       
        1
       
      
      
       x+1
      
     
    x+1行
    
     
      
       
        y
       
      
      
       y
      
     
    y列的整数表示连接农场
    
     
      
       
        x
       
      
      
       x
      
     
    x和农场
    
     
      
       
        y
       
      
      
       y
      
     
    y所需要的光纤长度。
【输出格式】
 输出一个整数,表示所需的最小光纤长度。
【数据范围】
 
    
     
      
       
        3
       
       
        ≤
       
       
        n
       
       
        ≤
       
       
        100
       
      
      
       3≤n≤100
      
     
    3≤n≤100
 每两个农场间的距离均是非负整数且不超过
    
     
      
       
        100000
       
      
      
       100000
      
     
    100000。
【输入样例】
4
0  4  9  21
4  0  8  17
9  8  0  16
21 17 16  0
 
【输出样例】
28
 
【分析】
模板题,当做复习一下Prim算法~
【代码】
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int g[N][N];
int st[N], dis[N];
int n;
int prim()
{
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    int res = 0;
    for (int i = 0; i < n; i++)
    {
        int t = -1;
        for (int j = 1; j <= n; j++)
            if (!st[j] && (!~t || dis[j] < dis[t])) t = j;
        res += dis[t];
        st[t] = 1;
        for (int j = 1; j <= n; j++) dis[j] = min(dis[j], g[t][j]);
    }
    return res;
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> g[i][j];
    cout << prim() << endl;
    return 0;
}
 
二、AcWing 1141. 局域网
【题目描述】
 某个局域网内有
    
     
      
       
        n
       
      
      
       n
      
     
    n台计算机和
    
     
      
       
        k
       
      
      
       k
      
     
    k条 双向 网线,计算机的编号是
    
     
      
       
        1
       
       
        ∼
       
       
        n
       
      
      
       1∼n
      
     
    1∼n。由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。
注意:
- 对于某一个连接,虽然它是双向的,但我们不将其当做回路。本题中所描述的回路至少要包含两条不同的连接。
 - 两台计算机之间最多只会存在一条连接。
 - 不存在一条连接,它所连接的两端是同一台计算机。
 
因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用 f ( i , j ) f(i,j) f(i,j)表示 i , j i,j i,j之间连接的畅通程度, f ( i , j ) f(i,j) f(i,j)值越小表示 i , j i,j i,j之间连接越通畅。
现在我们需要解决回路问题,我们将除去一些连线,使得网络中没有回路且不影响连通性(即如果之前某两个点是连通的,去完之后也必须是连通的),并且被除去网线的 Σ f ( i , j ) Σf(i,j) Σf(i,j)最大,请求出这个最大值。
【输入格式】
 第一行两个正整数
    
     
      
       
        n
       
       
        ,
       
       
        k
       
      
      
       n,k
      
     
    n,k。
 接下来的
    
     
      
       
        k
       
      
      
       k
      
     
    k行每行三个正整数
    
     
      
       
        i
       
       
        ,
       
       
        j
       
       
        ,
       
       
        m
       
      
      
       i,j,m
      
     
    i,j,m表示
    
     
      
       
        i
       
       
        ,
       
       
        j
       
      
      
       i,j
      
     
    i,j两台计算机之间有网线联通,通畅程度为
    
     
      
       
        m
       
      
      
       m
      
     
    m。
【输出格式】
 一个正整数,表示被除去网线的
    
     
      
       
        Σ
       
       
        f
       
       
        (
       
       
        i
       
       
        ,
       
       
        j
       
       
        )
       
      
      
       Σf(i,j)
      
     
    Σf(i,j)的最大值。
【数据范围】
 
    
     
      
       
        1
       
       
        ≤
       
       
        n
       
       
        ≤
       
       
        100
       
      
      
       1≤n≤100
      
     
    1≤n≤100
 
    
     
      
       
        0
       
       
        ≤
       
       
        k
       
       
        ≤
       
       
        200
       
      
      
       0≤k≤200
      
     
    0≤k≤200
 
    
     
      
       
        1
       
       
        ≤
       
       
        f
       
       
        (
       
       
        i
       
       
        ,
       
       
        j
       
       
        )
       
       
        ≤
       
       
        1000
       
      
      
       1≤f(i,j)≤1000
      
     
    1≤f(i,j)≤1000
【输入样例】
5 5
1 2 8
1 3 1
1 5 3
2 4 5
3 4 2
 
【输出样例】
8
 
【分析】
本题可能存在多个连通块,需要我们去掉权值尽可能大的边,且不影响每个连通块内各点的连通性。即需要求出最小的“生成森林”。我们在做Kruskal算法时,当某条边的两个端点已经被连通时,说明该边需要去除,将 r e s res res加上这条边的权值即可。
【代码】
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 210;
int pre[N];
int n, m;
struct Edge
{
    int x, y, w;
    bool operator< (const Edge& t) const
    {
        return w < t.w;
    }
}e[M];
int find(int k)
{
    if (pre[k] == k) return k;
    return pre[k] = find(pre[k]);
}
int kruskal()
{
    sort(e, e + m);
    int res = 0;
    for (int i = 0; i < m; i++)
    {
        int px = find(e[i].x), py = find(e[i].y);
        if (px != py) pre[px] = py;//x与y不在一个连通块中则连接
        else res += e[i].w;//x与y已经连通了说明这条边需要去掉
    }
    return res;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) pre[i] = i;
    for (int i = 0; i < m; i++) cin >> e[i].x >> e[i].y >> e[i].w;
    cout << kruskal() << endl;
    return 0;
}










