最小生成树
Kruskal算法
AcWing 859. Kruskal算法求最小生成树
题目链接
https://www.acwing.com/problem/content/861/
算法思想
- 定义结构体存边,重载运算符便于排序
- sort所有的边,从最小的边开始加,只要边连接的两个点不在同一个连通块中,就加入该条边
- 判断边链接的两个点是否在同一个连通块中,采用并查集维护
Ac代码
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10, M = 4 * N;
struct Edge{
int a, b, w;
bool operator<(const Edge &W) const{
return w < W.w;
}
}e[M];
int n, m;
int p[N];
int res, cnt;
int find(int x){
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int kruskal(){
sort(e, e + 2 * m);
for(int i = 1; i <= n; i ++) p[i] = i;
for(int i = 0; i < 2 * m; i ++){
int a = e[i].a, b = e[i].b, w = e[i].w;
int pa = find(a), pb = find(b);
if(pa != pb){
p[pa] = pb;
res += w;
cnt ++;
}
}
if(cnt < n - 1) return 0x3f3f3f3f;
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i ++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
e[i] = {a, b, c};
e[i + m] = {b, a, c};
}
int ans = kruskal();
if(ans == 0x3f3f3f3f) puts("impossible");
else printf("%d\n", ans);
return 0;
}
招募军队
题目链接
http://www.zjutacm.cn/problem/2018
解析
- 关于超级源点:仔细阅读上图体会思想,重点在于这里每个连通块中只有一个点是按照a[i]招募军队,反正就是问题转化地很好
- 要跑最大生成树和最小生成树,区别在于加边的时候从权值大的开始还是权值小的开始
Ac代码
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct Edge{
int a, b;
ll w;
bool operator < (const Edge &W) const {
return w < W.w;
}
}e[2 * N];
int n, m;
int p[N];
ll a[N];
int find(int x){
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
ll kruskal_min(){
ll res = 0;
for(int i = 0; i <= n; i ++) p[i] = i;
sort(e, e + m + n);
for(int i = 0; i < m + n; i ++){
int a = e[i].a, b = e[i].b;
ll w = e[i].w;
int pa = find(a), pb = find(b);
if(pa != pb){
p[pa] = pb;
res += w;
}
}
return res;
}
ll kruskal_max(){
ll res = 0;
for(int i = 0; i <= n; i ++) p[i] = i;
sort(e, e + m + n);
for(int i = m + n - 1; i >= 0; i --){
int a = e[i].a, b = e[i].b;
ll w = e[i].w;
int pa = find(a), pb = find(b);
if(pa != pb){
p[pa] = pb;
res += w;
}
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
ll s = 0;
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), s += a[i];
for(int i = 0; i < m; i ++){
int a, b; ll c;
scanf("%d%d%lld", &a, &b, &c);
e[i] = {a, b, c};
}
for(int i = 1; i <= n; i ++) e[m + i - 1] = {0, i, a[i]};
ll ans1 = kruskal_min();
ll ans2 = kruskal_max();
//cout << ans1 << ' ' << ans2 << endl;
printf("%lld\n", max(s - ans1, ans2 - s));
return 0;
}
ABC235 E - MST + 1
题目链接
https://atcoder.jp/contests/abc235/tasks/abc235_e
解析
- 重点在于理解kruskl算法构造最小生成树的思想
- 存储询问,在kruskal的过程中判断询问中的边是否可能被加入
- 本题中结构体的定义比较关键,除了基本的点和权值之外,还应该维护一个tag(记录是询问还是原图中的边)和id(表示在询问中的顺序,便于答案输出)
Ac代码
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10, M = 4e5 + 10;
struct Edge
{
int a, b, w, tag, x;
bool operator < (const Edge &p){
return w < p.w;
}
}e[M], q[M];
int n, m, qq;
int fa[N];
bool ans[N];
int find_(int x)
{
if(fa[x] == x) return x;
return fa[x] = find_(fa[x]);
}
void kruskal()
{
sort(e, e + m + qq);
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 0; i < m + qq; i ++){
int a = e[i].a , b = e[i].b, w = e[i].w;
a = find_(a);
b = find_(b);
if(a != b){
if(e[i].tag == 1) ans[e[i].x] = true;
else fa[a] = b;
}
}
return;
}
int main()
{
scanf("%d%d%d", &n, &m, &qq);
for(int i = 0; i < m; i ++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
e[i] = {a, b, c, 0};
//cout << e[i].ans << endl;
}
for(int i = 0; i < qq; i ++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
e[i + m] = {a, b, c, 1, i};
}
kruskal();
for(int i = 0; i < qq; i ++){
if(ans[i]) puts("Yes");
else puts("No");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!