zoukankan      html  css  js  c++  java
  • 【Luogu3366】模板:最小生成树

    Link:
    Luogu https://www.luogu.com.cn/problem/P3366


    这波啊,这波是高强度回忆青春



    §1 Prim


    Prim 算法的时间复杂度为 (O(n^2+m))
    从一个结点开始扩散最小生成树,每次把树直接接着的最短边塞进来。
    正确性比较显然,把图割成两半即可。

    用数据结构优化一下找可行最短边,时间复杂度可以达到 (O((n+m)log n))

    本来以为代码会被卡,没想到一次过了(
    另外粗略感觉可以用线段树,等我睡醒上线摸余
    下面是 我最喜欢写的 优先队列

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    using namespace std;
    const int MAXM = 4e5 + 20;
    const int MAXN = 5010;
    int n, m, tot = 0;
    int nxt[MAXM], head[MAXN], val[MAXM], to[MAXM];
    long long Ans = 0;
    bool vis[MAXN];
    int mini[MAXN];
    #define PII pair<int, int>
    priority_queue<PII, vector<PII>, greater<PII> > Q;
    #define add_edge(u, v, w) nxt[++tot]=head[u], head[u]=tot, to[tot]=v, val[tot]=w
    void Update(const int& x)
    {
    	for (int i = head[x]; i; i = nxt[i])
    	{
    		if (vis[to[i]]) continue;
    		if (val[i] >= mini[to[i]]) continue;
    		mini[to[i]] = val[i];
    		Q.push(make_pair(val[i], to[i]));
    	}
    }
    int fre;
    void Prim()
    {
    	fre = n - 1;
    	Update(1);
    	vis[1] = true;
    
    	PII pp;
    	while (Q.size())
    	{
    		pp = Q.top(); Q.pop();
    		
    		if (vis[pp.second]) continue;
    		vis[pp.second] = true; --fre;
    		Ans += pp.first;
    		
    		if (!fre) break;
    		Update(pp.second);
    	}
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin >> n >> m;
    	memset(mini, 0x3f, sizeof(mini));
    	for (int u, v, w, i = 1; i <= m; ++i)
    	{
    		cin >> u >> v >> w;
    		add_edge(u, v, w);
    		add_edge(v, u, w);
    	}
    	Prim();
    	if (Q.empty() && fre)
    	{
    		cout << "orz";
    		return 0;
    	}
    	cout << Ans;
    	return 0;
    }
    


    §2 Kruskal


    Kruskal 算法的时间复杂度为 (O(mlog m))
    按照边权从小到大 竹节虫 搭最小生成树
    用并查集判断连一条新边之后能不能还是树

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    using namespace std;
    #define rep(ii,jj,kk) for(int ii=jj;ii<=kk;++ii)
    const int MAXN = 5005;
    const int MAXM = 2e5 + 10;
    int n, m, tot = 0;
    int fa[MAXN], siz[MAXN];
    long long Ans = 0;
    int findfa(int x)
    {
    	return fa[x] == x ? x : (fa[x] = findfa(fa[x]));
    }
    bool merge(int x, int y)
    {
    	static int fx, fy;
    	fx = findfa(x);
    	fy = findfa(y);
    	if (fx == fy) return false;
    	if(siz[x] < siz[y]) fa[fx] = fy, siz[fy] += siz[fx];
    	else fa[fy] = fx, siz[fx] += siz[fy];
    	return true;
    }
    struct Edge
    {
    	int ptf/*from*/, ptt/*to*/, ptv/*value*/;
    	Edge(int aug1 = 0, int aug2 = 0, int aug3 = 0)
    	{
    		ptf = aug1;
    		ptt = aug2;
    		ptv = aug3;
    	}
    }qf;
    priority_queue<Edge, vector<Edge>, greater<Edge> >Q;
    bool operator > (const Edge &a, const Edge &b)
    {
    	return a.ptv > b.ptv;
    }
    int main()
    {
    	scanf("%d%d", &n, &m);
    	rep(i, 1, n) siz[i] = 1, fa[i] = i;
     	for(int u, v, w, i = 1; i <= m; ++i)
    	{
    		scanf("%d%d%d", &u, &v, &w);
    		Q.push(Edge(u, v, w));
    	}
    	while(!Q.empty())
    	{
    	    qf = Q.top(); Q.pop();
    	    if(merge(qf.ptf, qf.ptt)) Ans += qf.ptv;
    	}
            if(siz[findfa(1)] == n)
            {
            	printf("%lld", Ans);
            }
            else puts("orz");
            return 0;
    }
    


    §3 Borůvka


    哇哦.jpg
    没啥太大的实用价值

    云了一下,就是一开始每个点自成一个连通块。
    每次把每个连通块往外找一条最短的边伸出去,就会让两个连通块被连起来,用并查集合并。
    直到只剩下一个连通块。
    这样的“每次”是 (O(log n))
    复杂度是 (O((n+m)log n))


    §4 Prim分块


    (O(nsqrt n+m))
    分块不愧是唯一神。

    没必要专门分成连通块,直接按编号分块也可
    其他的自己yy就能写出来啦。

  • 相关阅读:
    crash收集上报方案
    keychain的使用
    自定义Xcode文件模板
    iOS实现一个简单的扫码功能
    tableView渲染延迟
    iOS app icons
    fastlane自动打包
    iOS pod封装和升级
    手写代码 -- 数组扁平化
    手写代码 -- Promise
  • 原文地址:https://www.cnblogs.com/ccryolitecc/p/13870288.html
Copyright © 2011-2022 走看看