洛谷-【图论2-3】最小生成树1

P3366 【模板】最小生成树

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​,Yi​,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

输入输出样例

输入 #1复制

复制代码
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

输出 #1复制

复制代码
7

说明/提示

数据规模:

对于 20% 的数据,N≤5,M≤20。

对于 40% 的数据,N≤50,M≤2500。

对于 70% 的数据,N≤500,M≤104。

对于 100% 的数据:1≤N≤5000,1≤M≤2×105,1≤Zi​≤104,1≤Xi​,Yi​≤N。

样例解释:

所以最小生成树的总边权为 2+2+3=7。

实现代码:

cpp 复制代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int MaxN = 5000 + 5, MaxM = 200000 + 5;

int N, M;
int U[MaxM], V[MaxM], W[MaxM];
bool used[MaxM];
int par[MaxN], Best[MaxN];

void init() {
    scanf("%d %d", &N, &M);
    for (int i = 1; i <= M; ++i)
        scanf("%d %d %d", &U[i], &V[i], &W[i]);
}

void init_dsu() {
    for (int i = 1; i <= N; ++i)
        par[i] = i;
}

int get_par(int x) {
    if (x == par[x]) return x;
    else return par[x] = get_par(par[x]);
}

inline bool Better(int x, int y) {
    if (y == 0) return true;
    if (W[x] != W[y]) return W[x] < W[y];
    return x < y;
}

void Boruvka() {
    init_dsu();

    int merged = 0, sum = 0;

    bool update = true;
    while (update) {
        update = false;
        memset(Best, 0, sizeof Best);

        for (int i = 1; i <= M; ++i) {
            if (used[i] == true) continue;
            int p = get_par(U[i]), q = get_par(V[i]);
            if (p == q) continue;

            if (Better(i, Best[p]) == true) Best[p] = i;
            if (Better(i, Best[q]) == true) Best[q] = i;
        }

        for (int i = 1; i <= N; ++i)
            if (Best[i] != 0 && used[Best[i]] == false) {
                update = true;
                merged++; sum += W[Best[i]];
                used[Best[i]] = true;
                par[get_par(U[Best[i]])] = get_par(V[Best[i]]);
            }
    }

    if (merged == N - 1) printf("%d\n", sum);
    else puts("orz");
}

int main() {
    init();
    Boruvka();
    return 0;
}

P1194 买礼物

题目描述

又到了一年一度的明明生日了,明明想要买 B 样东西,巧的是,这 B 样东西价格都是 A 元。

但是,商店老板说最近有促销活动,也就是:

如果你买了第 I 样东西,再买第 J 样,那么就可以只花 KI,J​ 元,更巧的是,KI,J​ 竟然等于 KJ,I​。

现在明明想知道,他最少要花多少钱。

输入格式

第一行两个整数,A,B。

接下来 B 行,每行 B 个数,第 I 行第 J 个为 KI,J​。

我们保证 KI,J​=KJ,I​ 并且 KI,I​=0。

特别的,如果 KI,J​=0,那么表示这两样东西之间不会导致优惠。

注意 KI,J​ 可能大于 A。

输出格式

一个整数,为最小要花的钱数。

输入输出样例

输入 #1复制

复制代码
1 1
0

输出 #1复制

复制代码
1

输入 #2复制

复制代码
3 3
0 2 4
2 0 2
4 2 0

输出 #2复制

复制代码
7

说明/提示

样例解释 2。

先买第 2 样东西,花费 3 元,接下来因为优惠,买 1,3 样都只要 2 元,共 7 元。

(同时满足多个"优惠"的时候,聪明的明明当然不会选择用 4 元买剩下那件,而选择用 2 元。)

数据规模

对于 30% 的数据,1≤B≤10。

对于 100% 的数据,1≤B≤500,0≤A,KI,J​≤1000。

2018.7.25新添数据一组

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
struct node {
	int x,y,s;
} d[200000];
int n,m,ans,num,f[1000],w,k,c;
bool ok[1000];
bool cmp(node a,node b) {
	return a.s<b.s;
}
int gf(int x) {
	if(f[x]==x)return x;
	return f[x]=gf(f[x]); 
}
void work() { 
	int x,y;
	for(int i=1; i<=n; i++)f[i]=i;
	for(int i=1; i<=k-num; i++) {
		x=gf(d[i].x);
		y=gf(d[i].y);
		if(x!=y) {
			f[x]=y;
			c++;
			ans+=d[i].s;
		}

	}
}
int main() {
	cin>>m>>n;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++) {
			cin>>w;
			if(i>=j||w==0)continue;
			d[++k].x=i;
			d[k].y=j;
			d[k].s=w;
			if(d[k].s>m)num++;
		}
	sort(d+1,d+k+1,cmp);
	work();
	if(c==n-1)cout<<ans+m;
	else cout<<ans+(n-c)*m; 
	return 0;
}

P4180 [BJWC2010] 严格次小生成树

题目描述

小 C 最近学了很多最小生成树的算法,Prim 算法、Kruskal 算法、消圈算法等等。正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是 EM​,严格次小生成树选择的边集是 ES​,那么需要满足:(value(e) 表示边 e 的权值) ∑e∈EM​​value(e)<∑e∈ES​​value(e)。

这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

输入格式

第一行包含两个整数 N 和 M,表示无向图的点数与边数。

接下来 M 行,每行 3 个数 x,y,z 表示,点 x 和点 y 之间有一条边,边的权值为 z。

输出格式

包含一行,仅一个数,表示严格次小生成树的边权和。

输入输出样例

输入 #1复制

复制代码
5 6
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 

输出 #1复制

复制代码
11

说明/提示

数据中无向图不保证无自环

对于 50% 的数据, N≤2000,M≤3000。

对于 80% 的数据, N≤5×104,M≤105。

对于 100% 的数据, N≤105,M≤3×105,边权 ∈[0,109],数据保证必定存在严格次小生成树。

实现代码:

cpp 复制代码
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>

using namespace std;

typedef long long ll;

const ll INF = (1ll << 62);

#define DEBUG(x) std::cerr << #x << " = " << x << std::endl

#define int ll

inline ll read() {
    ll f = 1, x = 0;char ch;
    do {ch = getchar();if (ch == '-')f = -1;} while (ch > '9' || ch < '0');
    do {x = x * 10 + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9');
    return f * x;
}

const int MAX_N = 100000 + 50;
const int MAX_M = 300000 + 50;
const int MAX_F = 30 + 5;

struct EDGE {
	int to, next, w;
} edge[MAX_M << 1];

int head[MAX_N], cnt;

void addedge (int u, int v, int w) {
	edge[++cnt].to = v;
	edge[cnt].w = w;
	edge[cnt].next = head[u];
	head[u] = cnt;
}

int f[MAX_N][MAX_F], g[MAX_N][MAX_F], h[MAX_N][MAX_F], dep[MAX_N];

inline void ckmax (int &x, int y) {
	x = (x > y ? x : y);
}

inline void dfs (int u, int fa, int w) {
	dep[u] = dep[fa] + 1;
	f[u][0] = fa;
	g[u][0] = w;
	h[u][0] = -INF;
	for (int i = 1; i <= 20; i ++ ) {
		f[u][i] = f[f[u][i - 1]][i - 1];
		g[u][i] = max (g[u][i - 1], g[f[u][i - 1]][i - 1]);
		h[u][i] = max (h[u][i - 1], h[f[u][i - 1]][i - 1]);
		if (g[u][i - 1] > g[f[u][i - 1]][i - 1]) h[u][i] = max (h[u][i], g[f[u][i - 1]][i - 1]);
		else if (g[u][i - 1] < g[f[u][i - 1]][i - 1]) h[u][i] = max (h[u][i], g[u][i - 1]);
	}
	for (int i = head[u]; i; i = edge[i].next ) {
		int v = edge[i].to, w = edge[i].w;
		if (v == fa) continue;
		dfs (v, u, w);
	}
}

inline int LCA (int x, int y) {
	if (dep[x] < dep[y]) swap (x, y);
	for (int i = 20; i >= 0; i -- ) {
		if (dep[f[x][i]] >= dep[y]) x = f[x][i];
	}
	if (x == y) return x;
	for (int i = 20; i >= 0; i -- ) {
		if (f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

int n, m, fa[MAX_N], res, sum;

struct Node {
	int u, v, w;
	bool operator < (const Node & rhs ) const  {
		return w < rhs.w;
	}
} a[MAX_M << 1];

inline int find (int x) {
	return x == fa[x] ? x : fa[x] = find (fa[x]);
}

inline void merge (int x, int y) {
	x = find (x); y = find (y);
	if (x == y) return;
	fa[x] = y;
}

bool vis[MAX_M];

inline void kruskal () {
	n = read (); m = read ();
	for (int i = 1; i <= m; i ++ ) {
		a[i].u = read (); a[i].v = read (); a[i].w = read ();
	}
	sort (a + 1, a + m + 1);
	for (int i = 1; i <= n; i ++ ) fa[i] = i;
	res = 0;
	for (int i = 1; i <= m; i ++ ) {
		if (find (a[i].u) != find (a[i].v)) {
			vis[i] = 1;
			res ++ ;
			merge (a[i].u, a[i].v);
			sum += a[i].w;
			addedge (a[i].u, a[i].v, a[i].w);
			addedge (a[i].v, a[i].u, a[i].w);
		}
		if (res == n - 1) break;
	}
	res = 0;
	dfs (1, 0, 0);
}

inline int qmax (int u, int v, int maxx) {
	int ans = -INF;
	for (int i = 18; i >= 0; i -- ) {
		if (dep[f[u][i]] >= dep[v]) {
			if (maxx != g[u][i]) ans = max (ans, g[u][i]);
			else ans = max (ans, h[u][i]);
			u = f[u][i];
		}
	}
	return ans;
}

inline void ckmin (int &x, int y) {
	x = (x < y ? x : y);
}

inline void calc () {
	int ans = INF;
	for (int i = 1; i <= m; i ++ ) {
		if (vis[i]) continue;
		int lca = LCA (a[i].u, a[i].v);
		int max_u = qmax (a[i].u, lca, a[i].w);
		int max_v = qmax (a[i].v, lca, a[i].w);
		ckmin (ans, sum - max (max_u, max_v) + a[i].w);
	}
	printf ("%lld\n", ans);
}

signed main() {
	kruskal ();
	calc ();
	return 0;
}

P1396 营救

题目背景

"咚咚咚......""查水表!"原来是查水表来了,现在哪里找这么热心上门的查表员啊!小明感动得热泪盈眶,开起了门......

题目描述

妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富的经验告诉她小明被带到了 t 区,而自己在 s 区。

该市有 m 条大道连接 n 个区,一条大道将两个区相连接,每个大道有一个拥挤度。小明的妈妈虽然很着急,但是不愿意拥挤的人潮冲乱了她优雅的步伐。所以请你帮她规划一条从 s 至 t 的路线,使得经过道路的拥挤度最大值最小。

输入格式

第一行有四个用空格隔开的 n,m,s,t,其含义见【题目描述】。

接下来 m 行,每行三个整数 u,v,w,表示有一条大道连接区 u 和区 v,且拥挤度为 w。

两个区之间可能存在多条大道

输出格式

输出一行一个整数,代表最大的拥挤度。

输入输出样例

输入 #1复制

复制代码
3 3 1 3
1 2 2
2 3 1
1 3 3

输出 #1复制

复制代码
2

说明/提示

数据规模与约定
  • 对于 30% 的数据,保证 n≤10。
  • 对于 60% 的数据,保证 n≤100。
  • 对于 100% 的数据,保证 1≤n≤104,1≤m≤2×104,w≤104,1≤s,t≤n。且从 s 出发一定能到达 t 区。

样例输入输出 1 解释

小明的妈妈要从 1 号点去 3 号点,最优路线为 1→2→3。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t,a[20001];
struct each
{
    int x,y,cost;
}b[20001];
bool com(each x,each y)
{
    return x.cost<y.cost;
}
int read(){
    char ch=getchar();
    int x=0,f=1;
    while(ch<'0'||ch>'9')
        {
        if(ch=='-')
            f=-1;
        ch=getchar();
        }
    while(ch>='0'&&ch<='9')
        {
        x=x*10+ch-'0';
        ch=getchar();
        }
    return x*f;
}
int find(int x){
    if(a[x]==0)
        return x;
    a[x]=find(a[x]);
    return a[x];
}
int main(){
    n=read();
    m=read();
    s=read();
    t=read();
    for(int i=1;i<=m;i++)
        {
        b[i].x=read();
        b[i].y=read();
        b[i].cost=read();
        }
    sort(b+1,b+m+1,com);
    for(int i=1;i<=m;i++)
        {
        int X=find(b[i].x),Y=find(b[i].y);
        if(X!=Y)
            a[X]=Y;
        if(find(s)==find(t))
            {
            cout<<b[i].cost<<endl;
            return 0;
            }
        }
    return 0;
}

P1195 口袋的天空

题目背景

小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。

有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。

题目描述

给你云朵的个数 N,再给你 M 个关系,表示哪些云朵可以连在一起。

现在小杉要把所有云朵连成 K 个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。

输入格式

第一行有三个数 N,M,K。

接下来 M 行每行三个数 X,Y,L,表示 X 云和 Y 云可以通过 L 的代价连在一起。

输出格式

对每组数据输出一行,仅有一个整数,表示最小的代价。

如果怎么连都连不出 K 个棉花糖,请输出 No Answer

输入输出样例

输入 #1复制

复制代码
3 1 2
1 2 1

输出 #1复制

复制代码
1

说明/提示

对于 30% 的数据,1≤N≤100,1≤M≤103;

对于 100% 的数据,1≤N≤103,1≤M≤104,1≤K≤10,1≤X,Y≤N,0≤L<104。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int n,k,m;
int fa[1000050];
struct node {
	int x;
	int y;
	int l;
} a[1000005];
int cmp( const void *a , const void *b ) {
	struct node *c = (node *)a;
	struct node *d = (node *)b;
	return c->l - d->l;
}
int find(int x){
	if(x!=fa[x])
		fa[x]=find(fa[x]);
	return fa[x];
}
void work(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y)
		return;
	fa[x]=y;
}
int main(){
	cin>>n>>m>>k;
	for(int i=0; i<m; i++) {
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].l);
	}
	qsort(a,m,sizeof(a[0]),cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	int num=n-k;
	int ans=0;
	for(int i=0;i<m;i++)
	{
		if(num==0)
			break;
		int aaa=find(a[i].x);
		int wzx=find(a[i].y);
		if(aaa!=wzx)
		{
			work(a[i].x,a[i].y);
			ans+=a[i].l;
			num--;
		}
	}
	if(num)
		cout<<"No Answer"<<endl;
	else
		cout<<ans<<endl;
}
相关推荐
YL200404263 小时前
054实现Trie(前缀树)
数据结构·leetcode
砍材农夫3 小时前
物联网 基于netty构建mqtt协议规范(三种 QoS 等级)
java·开发语言·物联网
故事和你913 小时前
洛谷-【图论2-3】最小生成树2
开发语言·数据结构·c++·算法·动态规划·图论
甲方大人请饶命3 小时前
Java-IO流
java·开发语言
guygg883 小时前
贝叶斯非局部均值降噪算法C语言实现
c语言·算法·均值算法
郝学胜-神的一滴3 小时前
中级OpenGL教程 006:高光反射原理与 Shader 实现
c++·unity·godot·图形渲染·three.js·opengl·unreal
量子炒饭大师3 小时前
【优化算法】滑动窗口的「义体化」重构 ——【滑动窗口】何为滑动窗口?滑动窗口算法的核心目的是什么?
c++·算法·重构·优化算法·双指针·滑动窗口
玖釉-3 小时前
C++ 中的 buckets 详解:从哈希桶到 unordered_map 底层原理
算法·哈希算法·散列表
计算机安禾3 小时前
【c++面向对象编程】第35篇:构造函数与异常:如何避免资源泄露?
开发语言·javascript·c++·算法·性能优化