P3623 [APIO2008] 免费道路
P3623 [APIO2008] 免费道路
题目翻译:
给定一个无向联通图,图中的边有两种类型\(1\)或\(0\),求生成一棵树使得类型为\(0\)的边要为\(k\)个。
思路:
阅读题目发现,最后的图要是一棵树,使得上面边为\(0\)的个数为\(k\),我们可以想到先找到,\(k\)个边在找其他边,但又要满足图的联通,那我们运用最小生成树\(kruskal\)算法的思想,先将类型为\(1\)的边给进行加边,若继续加类型\(0\)的边,那这些边就是一定需要的,不管这么搭配,这些边就是必要的。那第二次跑\(kruskal\)时就先加剩下需要的类型为\(0\)的边,直到数量满足为\(k\)时停止,然后加类型为\(1\)的边直至联通。
无解判定:
1.若第一次跑\(kruskal\)时发现必须的边数超过了\(k\)那必然无解
2.若第二次跑完\(kruskal\)时发现无法组成一棵树,或总共加入的类型为零的边不及\(k\)个,则无解
完整代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int fa[N];
int n,m,k,cnt;
struct edge{
int u,v,w;
}e[N];
bool cmp1(edge x,edge y){
return x.w<y.w;
}
bool cmp2(edge x,edge y){
return x.w>y.w;
}
int find(int x){
if(fa[x]==x)return x;
else return fa[x]=find(fa[x]);
}
void kruskal1(){
for(int i=1;i<=n;i++){
fa[i]=i;
}
sort(e+1,e+1+m,cmp1);
for(int i=1;i<=m;i++){
int u=find(e[i].u);
int v=find(e[i].v);
if(u!=v){
fa[u]=v;
if(e[i].w==1){
cnt++;
e[i].w=3;
}
}
}
if(cnt>k){
cout<<"no solution"<<endl;
exit(0);
}
}
void kruskal2(){
int num=0;
for(int i=1;i<=n;i++){
fa[i]=i;
}
sort(e+1,e+1+m,cmp2);
for(int i=1;i<=m;i++){
int u=find(e[i].u);
int v=find(e[i].v);
if(find(u)!=find(v)){
if(e[i].w==1 && cnt==k){
continue;
}
num++;
fa[u]=v;
if(e[i].w==1 && cnt<k){
e[i].w=3;
cnt++;
}
else if(e[i].w==0){
e[i].w=-2;
}
}
}
if(num<n-1 || cnt<k){
cout<<"no solution"<<endl;
exit(0);
}
for(int i=1;i<=m;i++){
if(e[i].w==3)cout<<e[i].u<<" "<<e[i].v<<" "<<0<<endl;
if(e[i].w==-2)cout<<e[i].u<<" "<<e[i].v<<" "<<1<<endl;
}
}
signed main(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
w=!w;
e[i]={u,v,w};
}
kruskal1();
kruskal2();
}
最小生成树\(kruskal\)讲解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682384
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步