算法竞赛进阶指南——0x62【最小生成树】


在这里插入图片描述

Kruskal算法

用于稀疏图(即边少的图),用到并查集的知识

struct wt//**一击即中yes**//
{
    int x, y, z;
} e[N];
int n, m, f[N], cnt, sum;
**********************************************************************************
        for (int i = 1; i <= m; i++)
            cin >> e[i].x >> e[i].y >> e[i].z;//读入边
        for (int i = 1; i <= n; i++)
            f[i] = i;//初始化每个集合一个顶点
**********************************************************************************
int getfa(int v){return f[v] == v ? v : getfa(f[v]);}
bool merge(int a, int b)
{//归并,判断一条边的两点是否在一个集合中形成回路
    int fa = getfa(a);
    int fb = getfa(b);
    if (fa != fb)
    {
        f[fb] = fa;
        return 0;
    }
    else
        return 1;
}
int kruskal()//克鲁斯卡尔算法
{
    sort(e + 1, e + 1 + m, cmp);//先将边从小到大排序
    sum = 0;//最小生成树权值之和
    for (int i = 1; i <= m; i++)
    {
        if (!merge(e[i].x, e[i].y))//判断是否形成回路
        {
            sum += e[i].z;
            cnt++;//记录加入的边数
        }
        if (cnt == n - 1)//边数为n-1则退出循环
            break;
    }
    return sum;//返回权值之和
}

牛客一道小栗子走廊泼水节

大概题意是给一个最小生成树,让你求添加边构成完全图的最小权值【贪心地,考虑两个连通块,形成完全图需要a*b条边】

#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(3, "Ofast", "inline")
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4;
const int inf = 0x3f3f3f3f;
const int mod = 1e9;
#define IOS                  \
    ios::sync_with_stdio(0); \
    cin.tie(0);              \
    cout.tie(0);
ll t, n, ans;
ll f[N], siz[N];
struct edge
{
    ll u, v, w;
    bool operator<(const edge p) const { return w < p.w; }//运算符重载
} e[N];
ll getfa(ll v) { return f[v] == v ? v : f[v] = getfa(f[v]); }
void init()
{
    for (int i = 1; i <= n; i++)
    {
        f[i] = i;
        siz[i] = 1;
    }
    ans = 0;
}
void kruskal()
{
    init(); //注意初始化的位置
    sort(e + 1, e + n);//注意排序的位置
    for (int i = 1; i < n; i++)
    {

        ll fax = getfa(e[i].u), fay = getfa(e[i].v);
        if (fax != fay)//如果两个不在同一集合,则需都联通
        {
            ans += (siz[fax] * siz[fay] - 1) * (e[i].w + 1);//贪心+思维
            if (siz[fax] < siz[fay])
                swap(fax, fay);
            f[fay] = fax;
            siz[fax] += siz[fay];
        }
    }
}
int main()
{
    // IOS;
    scanf("%lld", &t);
    while (t--)
    {
        scanf("%lld", &n);
        for (int i = 1; i < n; i++)
        {
            ll u, v, w;
            scanf("%lld%lld%lld", &u, &v, &w);
            e[i].u = u, e[i].v = v, e[i].w = w;
        }
        kruskal();
        printf("%lld\n", ans);
    }
    return 0;
}

Prim算法

贪心算法,时间复杂度为O[N^2],用于稠密图(即边多的情况)可以用二叉堆优化

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
        e[i][j] = e[j][i] = (i == j ? 0 : INT_MAX);//初始化边***很重要
***************************************************************************************************
int n, m, cnt;       //n是顶点个数,m是边数
int e[N][N], dis[N]; //dis是每个顶点到最小生成树的最小距离
bool vis[N];         //将加入最小生成树的点标记
int prim()           //普里姆算法求最小生成树,不要轻易cnt++,有乱子
{
    int sum = 0; //最小生成树之和
    for (int i = 1; i <= n; i++)
        dis[i] = e[1][i];       //初始化每个顶点到最小生成树的距离为到顶点1的距离
    vis[1] = 1;                 //将1顶点加入最小生成树
    for (int i = 1; i < n; i++) //寻找剩余的n-1个顶点
    {
        int mif = 0, mi = INT_MAX;
        for (int j = 1; j <= n; j++) //寻找距离最小生成树最近的顶点
        {
            if (dis[j] < mi && !vis[j]) //找到且该点未在生成树中
            {
                mi = dis[j];
                mif = j; //记录该点
            }
        }
        sum += dis[mif];             //之和累加
        vis[mif] = 1;                //将该点标记,加入最小生成树中
        for (int j = 1; j <= n; j++) //更新每个未在生成树中的点到最小生成树的最近距离
        {
            if (!vis[j] && e[mif][j] < dis[j])
                dis[j] = e[mif][j];
        }
    }
    return sum;
}

牛客一道小栗子黑暗城堡
即求最短路径生成树,题意是让你统计以1为源点的最短路径树的方案数

#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4 + 100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS                  \
    ios::sync_with_stdio(0); \
    cin.tie(0);              \
    cout.tie(0);
ll n, m, tot, ans = 1;
ll to[M], val[M], nex[M], head[M], cnt[N], dis[N];
bool vis[N];
void add(ll u, ll v, ll w)
{
    to[++tot] = v;
    val[tot] = w;
    nex[tot] = head[u];
    head[u] = tot;
}
void init(){memset(dis, inf, sizeof(dis));}
void dijkstra()
{
    init();
    priority_queue<pll, vector<pll>, greater<pll>> p;
    p.push({0, 1});//pair{dis[i],i}
    dis[1] = 0;
    while (p.size())
    {
        pll x = p.top();
        p.pop();
        if (vis[x.second])
            continue;
        for (int i = head[x.second]; i; i = nex[i])
        {
            ll y = to[i], z = val[i];
            if (dis[y] > dis[x.second] + z)
            {
                dis[y] = dis[x.second] + z;
                p.push({dis[y], y});
            }
        }
    }
}
int main()
{
    // IOS;
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        ll x, y, z;
        scanf("%lld%lld%lld", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
    }
    dijkstra();
    for (int i = 1; i <= n; i++)
    {
        for (int j = head[i]; j; j = nex[j])
        {
            ll y = to[j], z = val[j];
            if (dis[y] == dis[i] + z)
                cnt[y]++;
        }
    }
    // for (int i = 1; i <= n; i++)
    //     printf("%d ", cnt[i]);
    // puts("");
    for (int i = 2; i <= n; i++) //注意从2开始,
        ans = (ans * cnt[i]) % mod;
    printf("%lld\n", ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WTcrazy _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值