Kruskal+贪心思想BZOJ3624-[Apio2008]免费道路
Posted Yiyi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kruskal+贪心思想BZOJ3624-[Apio2008]免费道路相关的知识,希望对你有一定的参考价值。
国庆万岁!!!!!
【题目大意】
给定一张无向图,有两种边的类型为0和1。求一个最小生成树使得边0有k条。
【思路】
跑两次Kruskal。
第一次的时候优先选择边1,然后判断有哪些边0还不能连通,那么这些边0是必须要选取的。如果必须要选的边0大于k,那么直接输出无解。
第二次的时候先合并那么必须要选取的边0,然后在剩下的边0中左右还没有连通的里选取。如果把所有都选上了之后边0的数量还是没有到k,那么直接输出无解。
截下来按照普通Kruskal的方法把边1合并掉。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 using namespace std; 7 const int MAXN=20000+50; 8 const int MAXM=100000+50; 9 struct Edge 10 { 11 int u,v; 12 }edge[MAXM]; 13 int n,m,k; 14 int vis[MAXM]; 15 int L,R; 16 int fa[MAXN],h[MAXN]; 17 vector<int> mustchoose; 18 19 void initset(){for (int i=1;i<=n;i++) fa[i]=i,h[i]=1;} 20 21 int find(int x) 22 { 23 int r=x; 24 while (fa[r]!=r) r=fa[r]; 25 while (fa[x]!=r) 26 { 27 int tmp=fa[x]; 28 fa[x]=r; 29 x=fa[x]; 30 } 31 return r; 32 } 33 34 void unionset(int a,int b) 35 { 36 if (h[a]>=h[b]) 37 { 38 fa[b]=a; 39 if (h[a]==h[b]) h[a]++; 40 } 41 else fa[a]=b; 42 } 43 44 void init() 45 { 46 scanf("%d%d%d",&n,&m,&k); 47 L=0,R=m+1; 48 for (int i=1;i<=m;i++) 49 { 50 int u,v,c; 51 scanf("%d%d%d",&u,&v,&c); 52 if (c) edge[++L]=(Edge){u,v}; 53 else edge[--R]=(Edge){u,v}; 54 } 55 } 56 57 void solve() 58 { 59 int t=0; 60 initset(); 61 for (int i=1;i<=m;i++) 62 { 63 int fa=find(edge[i].u),fb=find(edge[i].v); 64 if (fa!=fb) 65 { 66 unionset(fa,fb); 67 if (i>=R) 68 { 69 vis[i]=1; 70 t++; 71 mustchoose.push_back(i); 72 } 73 } 74 } 75 if (t>k) 76 { 77 puts("no solution"); 78 return; 79 } 80 // 找出必须要选择的鹅卵石路 81 82 initset(); 83 for (int i=0;i<mustchoose.size();i++) 84 { 85 int fa=find(edge[mustchoose[i]].u),fb=find(edge[mustchoose[i]].v); 86 unionset(fa,fb); 87 } 88 for (int i=R;i<=m;i++) 89 if (t<k && !vis[i]) 90 { 91 int fa=find(edge[i].u),fb=find(edge[i].v); 92 if (fa!=fb) 93 { 94 unionset(fa,fb); 95 vis[i]=1; 96 t++; 97 } 98 } 99 if (t<k) 100 { 101 puts("no solution"); 102 return; 103 } 104 //先选择必须要的鹅卵石路,然后再用其他鹅卵石路填充 105 106 for (int i=1;i<=L;i++) 107 { 108 int fa=find(edge[i].u),fb=find(edge[i].v); 109 if (fa!=fb) 110 { 111 unionset(fa,fb); 112 vis[i]=1; 113 } 114 } 115 for (int i=1;i<=L;i++) if (vis[i]) printf("%d %d %d\n",edge[i].u,edge[i].v,1); 116 for (int i=R;i<=m;i++) if (vis[i]) printf("%d %d %d\n",edge[i].u,edge[i].v,0); 117 } 118 119 int main() 120 { 121 init(); 122 solve(); 123 }
以上是关于Kruskal+贪心思想BZOJ3624-[Apio2008]免费道路的主要内容,如果未能解决你的问题,请参考以下文章
Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)
BZOJ 3624 [Apio2008]免费道路:并查集 + 生成树 + 贪心恰有k条特殊路径