HDU Red/Blue Spanning Tree【生成树】
题意: 给出一个有N 个节点,和M 条边,这些边分为 蓝色和红色的,问是否存在一种情况为 恰好用 k个蓝色的边,和已有的若干条红色边,将原图连成一棵树。
分析: 两次 Kruskal ,第一次的时候先把能用的红色边全部用上,然后用蓝色边,这些用到的蓝色边都是最终的生成树所必需的,将其标记
如果发现需要的蓝色边数目大于 K,或用完了能用的蓝色边之后图还未连通,则不存在符合条件的情况
第二次Kruskal的时候,先把之前标记的蓝色边全部用上,然后在剩下的蓝色边选出能用的边,如果总数能达到K则存在符合的情况。
#include<stdio.h> #include<string.h> #include<stdlib.h> struct Edge { int from,to,co; }b[3000000],r[3000000]; int bn,rn; int f[1005]; int v[3000005]; int find(int x) { return f[x]==x?x:(f[x]=find(f[x])); } void join(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) { if(fx<fy) f[fy]=fx; else f[fx]=fy; } } int n,tt,k; int kruskal_1() { int i,x,y; for(i=1;i<=n;i++) f[i]=i; tt=0; for(i=0;i<rn;i++) { x=find(r[i].from); y=find(r[i].to); if(x!=y) join(x,y); } for(i=0;i<bn;i++) { x=find(b[i].from); y=find(b[i].to); if(x!=y) { tt++; join(x,y); v[i]=1; } } int tmp=find(1); for(i=1;i<=n;i++) if(find(i)!=tmp) return 0; if(tt>k) return 0; return 1; } int kruskal_2() { if(tt==k)return 1; int i,x,y; for(i=1;i<=n;i++) f[i]=i; for(i=0;i<bn;i++) if(v[i]) join(b[i].from,b[i].to); for(i=0;i<bn;i++) { if(v[i]) continue; x=find(b[i].from); y=find(b[i].to); if(x!=y) { tt++; join(x,y); if(tt==k) return 1; } } return 0; } int main() { int m,i,j,aa,bb; char s[2]; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { if(n==0&&m==0&&k==0) break; bn=rn=0; for(i=0;i<m;i++) { scanf("%s%d%d",s,&aa,&bb); if(s[0]=='B') { b[bn].from=aa; b[bn++].to=bb; } else { r[rn].from=aa; r[rn++].to=bb; } } memset(v,0,sizeof(v)); if(kruskal_1()==0) { printf("0\n"); continue; } if(kruskal_2()) printf("1\n"); else printf("0\n"); } return 0; }