【BZOJ3624】【APIO2008】免费道路 [生成树][贪心]
免费道路
Time Limit: 2 Sec Memory Limit: 128 MB[Submit][Status][Discuss]
Description
Input
Output
Sample Input
5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1
Sample Output
3 2 0
4 3 0
5 3 1
1 2 1
4 3 0
5 3 1
1 2 1
HINT
1<=n<=20000,1<=m<=100000,0<=k<=n-1
Main idea
一种0边,一种1边,求一棵最小生成树并且正好有K条0边,输出其中一种方案。
Solution
显然要搞一棵符合题目的生成树。
每次要加入0边或者1边,直接做肯定不可行,考虑有什么0边是一定要加入的。
只需要输出一种方案,所以我们先加入所有可加的1边,如果图不联通则加入可加入的0边,那么这几条0边在我们所求的方案中是一定需要加入的。
这时候判断一下,如果此时加入的0边数量>K,或者图还是无法联通的话则无解。然后处理完毕前半部分,考虑接下来如何实现。
因为我们要使得图为树并且正好有K条0边,运用贪心,想到了加入0边到K条位置(如果到不了K条则也无解),然后剩下的用1边来填。
验证一下这样做的可行性:由于我们在前半部分使得了可以成为一棵树,那么显然我们在后半部分中每加入一条0边,则在前半部分中一定有一条1边可以替换使得可行(因为前半部分是尽量加入1边)。每次连边判环运用Krusal即可。
Code
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 using namespace std; 8 9 const int ONE=1000001; 10 const int INF=2147483640; 11 12 int n,m,k; 13 int Edge_k; 14 int fa[ONE]; 15 int num; 16 int Choose[ONE]; 17 int ans_num; 18 int the0; 19 20 struct power 21 { 22 int x,y,v; 23 }a[ONE],Ans_edg[ONE]; 24 25 26 27 int get() 28 { 29 int res,Q=1; char c; 30 while( (c=getchar())<48 || c>57) 31 if(c=='-')Q=-1; 32 if(Q) res=c-48; 33 while((c=getchar())>=48 && c<=57) 34 res=res*10+c-48; 35 return res*Q; 36 } 37 38 int find(int x) 39 { 40 if(fa[x]!=x) fa[x]=find(fa[x]); 41 return fa[x]; 42 } 43 44 void Un(int a,int b) 45 { 46 int a1=find(a); 47 int b1=find(b); 48 if(a1!=b1) fa[a1]=b1; 49 } 50 51 int Add_set(int N,int v,int ci) 52 { 53 int kd=0; 54 for(int i=1;i<=m;i++) 55 { 56 if(kd>=N) break; 57 58 if(a[i].v!=v) continue; 59 60 int x=a[i].x,y=a[i].y; 61 if(find(x)!=find(y)) 62 { 63 Un(x,y); 64 if(ci>=2) Ans_edg[++ans_num]=a[i]; 65 if(ci==2) 66 { 67 Choose[++num]=i; 68 } 69 Edge_k++; 70 kd++; 71 if(ci==3) the0++; 72 } 73 if(Edge_k==n-1) break; 74 } 75 } 76 77 int main() 78 { 79 n=get(); m=get(); k=get(); 80 for(int i=1;i<=n;i++) fa[i]=i; 81 for(int i=1;i<=m;i++) 82 { 83 a[i].x=get(); a[i].y=get(); a[i].v=get(); 84 } 85 Edge_k=0; 86 87 Add_set(INF,1,1); 88 Add_set(INF,0,2); 89 90 if(Edge_k<n-1 || num>k) 91 { 92 printf("no solution\n"); 93 return 0; 94 } 95 96 Edge_k=0; 97 for(int i=1;i<=n;i++) fa[i]=i; 98 for(int i=1;i<=num;i++) 99 { 100 int x=Choose[i]; 101 Un(a[x].x,a[x].y); 102 Edge_k++; 103 if(Edge_k==n-1) break; 104 } 105 106 Add_set(k-num,0,3); 107 if(the0!=k-num) 108 { 109 printf("no solution\n"); 110 return 0; 111 } 112 113 Add_set(INF,1,4); 114 for(int i=1;i<=ans_num;i++) 115 { 116 printf("%d %d %d\n",Ans_edg[i].x,Ans_edg[i].y,Ans_edg[i].v); 117 } 118 119 }