Basic
从一个图里抽取一些边使其构成一棵树,且总边权最小
Function 01 [Kruskal]
贪心算法
选取当前最小边权的边
用并查集判断该边所连的点的连通性
Question 01 [ACP2023 最短网络]
模板题
Code
#include<bits/stdc++.h>
using namespace std;
const int N=107,M=10056;
int n,Distance[N][N],cnt,father[N],ans;
struct edge{
int ll,rr,w;
bool operator <(const edge &rhs)const{
return w<rhs.w;
}
}k[M];
int find(int x){
if(father[x]==x)return x;
return father[x]=find(father[x]);
}
void merge(int x,int y){
x=find(x),y=find(y);
father[x]=y;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)father[i]=i;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&Distance[i][j]);
for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)k[++cnt]={i,j,Distance[i][j]};
sort(k+1,k+cnt+1);
int shit=0;
for(int i=1;i<=cnt;i++){
if(shit==n-1)break;
if(find(k[i].ll)==find(k[i].rr))continue;
merge(k[i].ll,k[i].rr);
ans+=k[i].w;
++shit;
}
printf("%d",ans);
return 0;
}
Function 02 [Prim]
每次选择到已有连通块距离最近的点加入连通块
和 Dijstra 相似
Question 02 [ACP2024 局域网]
同样是模板
Code
#include<bits/stdc++.h>
using namespace std;
const int N=107,M=10009;
int n,k,tot,dis[N];
bool st[N]={};
struct edge{
int to,len;
};
struct node{
int id,len;
bool operator <(const node &rhs)const{
return len>rhs.len;
}
};
vector<edge> line[N];
priority_queue<node> q;
void Prim(int start){
dis[start]=0;
q.push({start,0});
while(!q.empty()){
node top=q.top();
q.pop();
if(st[top.id])continue;
st[top.id]=true;
tot-=top.len;
for(auto i:line[top.id]){
if(st[i.to])continue;
if(dis[i.to]>i.len){
dis[i.to]=i.len;
q.push({i.to,i.len});
}
}
}
}
int main(){
memset(dis,0x3f,sizeof dis);
int l,r,w;
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++){
scanf("%d%d%d",&l,&r,&w);
if(w==0)continue;
line[l].push_back({r,w});
line[r].push_back({l,w});
tot+=w;
}
Prim(1);
printf("%d",tot);
return 0;
}
Question 03 [ACP2025 繁忙的都市]
最小生成树一定是瓶颈生成树
仍然是模板
Code
#include<bits/stdc++.h>
using namespace std;
const int N=308,M=10098;
int n,m,Distance[N],big=-1;
bool st[N];
struct line{int to,len;};
struct node{
int id,len;
bool operator <(const node &rhs)const{
return len>rhs.len;
}
};
priority_queue<node> q;
vector<line> lable[N];
void Prim(int start){
Distance[start]=0;
q.push({start,0});
while(!q.empty()){
node top=q.top();
q.pop();
if(st[top.id])continue;
st[top.id]=true;
big=max(big,top.len);
for(auto it:lable[top.id]){
if(Distance[it.to]>it.len){
Distance[it.to]=it.len;
q.push({it.to,it.len});
}
}
}
}
int main(){
memset(Distance,0x3f3f3f,sizeof Distance);
int l,r,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d%d",&l,&r,&w),lable[l].push_back({r,w}),lable[r].push_back({l,w});
Prim(1);
printf("%d %d",n-1,big);
return 0;
}
Question 04 [ACP2026 联络员]
存在必选边的最小生成树
明显必须使用 Kruskal
必选边用并查集 Merge 并统计答案
可选边正常统计即可
Code
#include<bits/stdc++.h>
using namespace std;
const int N=2048,M=10047;
int n,m,father[N],cnt,ans;
int find(int x){
if(x==father[x])return x;
return father[x]=find(father[x]);
}
struct line{
int l,r,len;
bool operator <(const line &rhs)const{
return len<rhs.len;
}
}k[M];
int main(){
int op,l,r,len;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)father[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&op,&l,&r,&len);
if(op==1){
ans+=len;
father[find(l)]=find(r);
}else{
k[++cnt]={l,r,len};
}
}
sort(k+1,k+cnt+1);
for(int i=1;i<=cnt;i++){
if(find(k[i].l)==find(k[i].r))continue;
father[find(k[i].l)]=find(k[i].r);
ans+=k[i].len;
}
printf("%d",ans);
return 0;
}
Question 05 [连接格点]
强行连边跑模板即可
坐标需要哈希映射
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1023;
struct line{int l,r,len;}k[2*N*N];
int father[N*N],n,m,cnt,ans;
inline int hasher(int x,int y){
return x*m-m+y;
}
int find(int x){
if(x==father[x])return x;
return father[x]=find(father[x]);
}
bool cmp(line a,line b){
return a.len<b.len;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int a,b,c,d;
cin>>n>>m;
for(int i=1;i<=m*n;i++)father[i]=i;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
if(j<m)k[++cnt]={hasher(i,j),hasher(i,j+1),2};
if(i<n)k[++cnt]={hasher(i,j),hasher(i+1,j),1};
}
while(cin>>a>>b>>c>>d) {
father[find(hasher(a,b))]=find(hasher(c,d));
}
sort(k+1,k+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
if(find(k[i].l)==find(k[i].r))continue;
father[find(k[i].l)]=find(k[i].r);
ans+=k[i].len;
}
cout<<ans;
return 0;
}
Question 06 [ACP2098 新的开始]
Prim 算法妙用
将建发电站的费用直接存进 Distance 数组
跑一遍即可
因为加入连通块需要建发电站或拉电线
直接 Prim 就能 AC
Code
#include<bits/stdc++.h>
using namespace std;
const int N=3078;
int Distance[N],n,leng[N][N],ans;
bool st[N];
struct line{int to,len;};
struct node{
int id,len;
bool operator <(const node &rhs)const{
return len>rhs.len;
}
};
vector<line> mp[N];
void Prim(){
priority_queue<node> q;
for(int start=1;start<=n;start++)q.push({start,Distance[start]});
while(!q.empty()){
node top=q.top();
q.pop();
if(st[top.id])continue;
st[top.id]=true;
ans+=top.len;
for(auto it:mp[top.id]){
if(Distance[it.to]>it.len){
Distance[it.to]=it.len;
q.push({it.to,it.len});
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&Distance[i]);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&leng[i][j]);
for(int i=1;i<=n;i++)for(int j=1;j<i;j++)mp[i].push_back({j,leng[i][j]}),mp[j].push_back({i,leng[i][j]});
Prim();
printf("%d",ans);
return 0;
}
Question 07 [ACP2101 Tree]
考虑将所有的白色边都加上一个边权 w 跑 Kruskal
可知 w 越大白色边的数量越小
Code
Question 08 [ACP2103 次小生成树]
很好的一道题
让你知道你对最小生成树一无所知
所以怎么写呢
首先 Kruskal 出最小生成树
然后根据玄学
我们改一条边就是次小生成树
(我无法证明,但当你开始 Coding 之后你就会发现这一切并不重要)
根据科学
这样会导致一条环的诞生
而显然这条环的其他边就是新连接边的两个端点到他们的 LCA
而最大值和次大值都可以用倍增法求出
(如果你已经忘了倍增请先打一遍 [P3379 LCA])
(如果大佬会树链剖分请 dis 作者并接受作者的 %)
但次大值怎么求呢
我们考虑合并两段的最大值和次大值
设两段数列的最大值和次大值分别为 Max_1 Hmax_1 Max_2 Hmax_2
而最终的最大值和次大值为 Ultramax Ultrahmax
显然 Ultramax=max(Max_1,Max_2)
而 Ultrahmax 怎么求呢
当 Max_1==Max_2 时
Ultrahmax=max(Hmax_1,Hmax_2)
而若 Max_1!=Max_2
Ultrahmax 却不一定等于 min(Max_1,Max_2)
因为可能会存在 min(Max_1,Max_2) < max(Hmax_1,Hmax_2) 的情况
所以修正后的局部 Code:
int solve(int a,int b,int c,int d){
if(a==c){
return max(b,d);
}else{
if(min(a,c)<=max(b,d))return max(b,d);
else return min(a,c);
}
}
Code
#include<bits/stdc++.h>
using namespace std;
const int N=100087,M=300779,LIM=25,INF=1e9+7;
int n,m;
long long ans;
bool st[M];
struct edge{
int l,r,len;
bool operator <(const edge &rhs)const{return len<rhs.len;}
}k[M];
struct tree_node{int length,depth,maxi[35],father[35],hmaxi[35];}tree[N];
inline tree_node _father(tree_node tn,int level){return tree[tn.father[level]];}
struct line{int to,len;};
vector<line> lable[N];
int solve(int a,int b,int c,int d){
if(a==c){
return max(b,d);
}else{
if(min(a,c)<=max(b,d))return max(b,d);
else return min(a,c);
}
}
namespace Father{
int father[N];
int find(int x){
if(father[x]==x)return x;
return father[x]=find(father[x]);
}
void merge(int a,int b){
a=find(a),b=find(b);
father[a]=b;
}
};
void Kruskal(){
for(int i=1;i<=n;i++)Father::father[i]=i;
sort(k+1,k+m+1);
for(int i=1;i<=m;i++){
if(Father::find(k[i].l)==Father::find(k[i].r))continue;
Father::merge(k[i].l,k[i].r);
ans+=k[i].len;
st[i]=true;
lable[k[i].l].push_back({k[i].r,k[i].len});
lable[k[i].r].push_back({k[i].l,k[i].len});
}
}
void Tree(int id,int father,int length,int depth){
tree[id].father[0]=father;
tree[id].maxi[0]=length;
tree[id].hmaxi[0]=-INF;
tree[id].length=length;
tree[id].depth=depth;
for(int i=1;i<=LIM;i++){
tree[id].father[i]=_father(tree[id],i-1).father[i-1];
tree[id].maxi[i]=max(tree[id].maxi[i-1],_father(tree[id],i-1).maxi[i-1]);
tree[id].hmaxi[i]=solve(tree[id].maxi[i-1],tree[id].hmaxi[i-1],_father(tree[id],i-1).maxi[i-1],_father(tree[id],i-1).hmaxi[i-1]);
}
for(auto i:lable[id]){
if(i.to==father)continue;
Tree(i.to,id,i.len,depth+1);
}
}
int LCA(int a,int b,int &maxi1,int &hmaxi1,int &maxi2,int &hmaxi2){
maxi1=-INF,hmaxi1=-INF,maxi2=-INF,hmaxi2=-INF;
if(tree[a].depth>tree[b].depth)swap(a,b);
for(int i=LIM;i>=0;i--)if(tree[a].depth<=_father(tree[b],i).depth){
hmaxi2=solve(maxi2,hmaxi2,tree[b].maxi[i],tree[b].hmaxi[i]);
maxi2=max(maxi2,tree[b].maxi[i]);
b=tree[b].father[i];
}
if(a==b)return a;
for(int i=LIM;i>=0;i--)if(tree[a].father[i]!=tree[b].father[i]){
hmaxi1=solve(maxi1,hmaxi1,tree[a].maxi[i],tree[a].hmaxi[i]);
maxi1=max(maxi1,tree[a].maxi[i]);
hmaxi2=solve(maxi2,hmaxi2,tree[b].maxi[i],tree[b].hmaxi[i]);
maxi2=max(maxi2,tree[b].maxi[i]);
a=tree[a].father[i];
b=tree[b].father[i];
}
hmaxi1=solve(maxi1,hmaxi1,tree[a].maxi[0],tree[a].hmaxi[0]);
maxi1=max(maxi1,tree[a].maxi[0]);
hmaxi2=solve(maxi2,hmaxi2,tree[b].maxi[0],tree[b].hmaxi[0]);
maxi2=max(maxi2,tree[b].maxi[0]);
return tree[a].father[0];
}
void Handle_Second(){
int supermaxi=-INF,superhmaxi=-INF,tmp1,tmp2,tmp3,tmp4,answer=INF;
for(int i=1;i<=m;i++){
if(st[i])continue;
LCA(k[i].l,k[i].r,tmp1,tmp2,tmp3,tmp4);
supermaxi=max(tmp1,tmp3);
superhmaxi=solve(tmp1,tmp2,tmp3,tmp4);
if(supermaxi!=k[i].len){
answer=min(answer,k[i].len-supermaxi);
}else{
answer=min(answer,k[i].len-superhmaxi);
}
}
printf("%lld",ans+answer);
}
int main(){
int l,r,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&l,&r,&w);
k[i]={l,r,w};
}
Kruskal();
for(int i=0;i<=LIM;i++){
tree[1].father[i]=-1;
tree[1].maxi[i]=-INF;
tree[1].hmaxi[i]=-INF;
}
Tree(1,-1,-INF,0);
Handle_Second();
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 本地部署 DeepSeek:小白也能轻松搞定!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 从 Windows Forms 到微服务的经验教训
· 李飞飞的50美金比肩DeepSeek把CEO忽悠瘸了,倒霉的却是程序员
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee