算法提高-图论- 最小生成树

最小生成树

AcWing 1140. 最短网络

#include <iostream>
#include <cstring>

using namespace std;

const int N = 110;

int w[N][N];
int dist[N];
bool st[N];
int n;

int prime()
{
    int res = 0;
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    //和dj一样 这里1号节点虽然是最短的,但是我们需要它去更新其他边,因此不能st[1] = true;
    for (int i = 0; i < n; i ++ )//和dj一样,这里循环n次才能把n个节点加进最小生成树
    {
        int t = -1;
        
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
                
        st[t] = true;
        res += dist[t];
        
        for (int j = 1; j <= n; j ++ )
        {
            dist[j] = min(dist[j], w[t][j]);//这里不用dist[t] + w[t][j],这是求一个点到一个集合的距离,后者是求点j到源点的距离
        }
    }
    return res;
}

int main ()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )//题目要的答案和节点的下标无关,这里能把它存下来就行了
        for (int j = 1; j <= n; j ++ )
            cin >> w[i][j];
    
    cout << prime();
    return 0;
}

AcWing 1141. 局域网

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110, M = 210;

struct Edge
{
    int a, b, w;
    bool operator < (const Edge &t)const
    {
        return w < t.w;
    }
}e[M];

int p[N];//并查集父类数组
int n, m;

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];//return x 也是一样的
}
int main ()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ ) p[i] = i;//并查集初始化(这个经常忘),这个下标i是有意义的,要根据题目给的节点编号来
    
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        cin >> a >> b >> w;
        //e[i].a = a, e[i].b = b, e[i].w = w;
        e[i] = {a, b, w};
    }
    
    sort(e, e + m);
    int res = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = find(e[i].a), b = find(e[i].b), w = e[i].w;//后面不用再find了,因为我们a,b在定义的时候就是他们所属的集合
    
        if(a != b) p[a] = b;
        else res += w;
    }
    
    cout << res;
    return 0;
}

AcWing 1142. 繁忙的都市

和上题一摸一样,但是题意要求的out看起来需要特别处理,其实只要想清楚kruskal的性质就好

//kruskal本身就具有选择的路的权值中最大的权值尽量小这个性质,因为我们边是排序了的
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 310, M = 8010;

struct Edge
{
    int a, b, w;
    bool operator < (struct Edge &t)
    {
        return w < t.w;
    }
}e[M];

int n, m;
int p[N];

int find (int x)
{
    if (p[x] != x) p[x] = find (p[x]);
    return p[x];
}

int main ()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        cin >> a >> b >> w;
        e[i] = {a, b, w};
    }
    
    sort(e, e + m);
    int cnt = 0, res = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
        
        if (a != b) 
        {
            p[a] = b;
            cnt ++ ;
            res = w;
        }
    }
    
    cout << cnt << " " << res ;
    return 0;
}

AcWing 1143. 联络员

//1.用到了缩点,但本题的是无形中的,一个联通块的缩成了它的父节点
//2.知道了kurskal有多牛,可以解决已经有一些边的最小生成树问题,也可以解决只找一部分边的最小生成树的问题
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2010, M = 10010;

struct Edge
{
    int a, b, w;
    bool operator < (struct Edge & t)
    {
        return w < t.w;
    }
}e[M];

int p[N];
int n, m;

int find (int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int main ()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    int res = 0, k = 0;
    
    for (int i = 0; i < m; i ++ ) 
    {
        int t, a, b, w;
        cin >> t >> a >> b >> w;
        if (t == 1)
        {
            p[find(a)] = find(b);//先建立必须要选择的边
            res += w;
        }
        else 
        {
            e[k ++ ] = {a, b, w};//不是必须要选择的再加入e数组
        }
    }
    
    sort(e, e + k);
    for (int i = 0; i < k; i ++ )
    {
        int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
        if (a != b)
        {
            p[a] = b;
            res += w;
        }
    }
    cout << res;
    return 0;
}

AcWing 1144. 连接格点

题意就是求最小生成树,因为边权都是正数,那么我们多选一条边必然会导致我们的代价增大
但如果题目说了边权为正或者负数,那么就不是最小生成树了,极端一点所有边都是负的,那么我们为了代价最小肯定是所有边都选

//有点类似于拯救大兵那题
#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e3 + 10, M = N * N, K = 2 * N * N;//M:点数, K = 边数

struct  Edge
{
    int a, b, w;
}e[K];
int p[M];
int ids[N][N], cnt;//将二维坐标映射成1维
int n, m, k;

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void get_edges()
{
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, -1, 0, 1}, dw[4] = {1, 2, 1, 2};//必须要按1212的顺序枚举,这样u%2的时候就可以先添加1的边再添加2的边,自己排序少一个快排logn的复杂度
    for (int z = 0; z < 2; z ++ )//先枚举u%2余数为0的再枚举u%2余数为1的,余数为0说明是竖边权值为1,为1说明是横边权值为2
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )
                for (int u = 0; u < 4; u ++ )
                {
                    if (u % 2 == z)
                    {
                        int x = i + dx[u], y = j + dy[u], w = dw[u];
                        int a = ids[i][j], b = ids[x][y];
                        if (a < b) e[k ++ ] = {a, b, w};//因为是无向边我们枚举一个就行了
                    }
                }
}
int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n * m; i ++ ) p[i] = i;
    
    for (int i = 1;i <= n; i ++ )//将二维坐标映射成1维
        for (int j = 1; j <= m; j ++ )
            ids[i][j] = ++ cnt;
    
    int x1, x2, y1, y2;
    while(cin >> x1 >> y1 >> x2 >> y2)
    {
        int a = ids[x1][y1], b = ids[x2][y2];
        p[find(a)] = find(b);
    }
    
    get_edges();//初始化e[]
    
    
    int res = 0;
    for (int i = 0; i < k; i ++ )
    {
        int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
        if (a != b)
        {
            p[a] = b;
            res += w;
        }
    }
    
    cout << res;
    
    return 0;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值