bzoj3624(Apio2008):免费道路
题目↓
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
芒果君:很明显最小生成树。我一开始的想法有点接近正解(80分),如果一个点连接的边都是0边,而规定的额度k已经用完,就无法再跑kruskal,为了避免这种情况发生,我把这样的点连接的边权值改为-1(使得它们能被优先选)。改题的时候我想到如果一个“0点”连接了很多0边,它就会产生问题——有些与0点连接的点可以被1边更新,却用了0的额度,导致其他的0点无法被更新。
所以第一遍kruskal只用1边,找出0点(它是当前森林中树的祖宗,不一定只连接了0边,这也补了上面算法漏洞),再根据优先级跑第二遍。无解的判断需要特别注意。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<iostream> 6 #include<ctime> 7 #include<map> 8 #define maxn 20010 9 #define maxm 100010 10 using namespace std; 11 struct Edge{ 12 int u,v,w,f; 13 }e[maxm]; 14 int sign[maxn],fa[maxn],n,m,k,cnt,tot; 15 bool cmp1(Edge x,Edge y){return x.w>y.w;} 16 bool cmp2(Edge x,Edge y){return x.w<y.w;} 17 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 18 void kruskal(int f) 19 { 20 cnt=0; 21 for(int i=1;i<=n;++i) fa[i]=i; 22 for(int i=1;i<=m;++i){ 23 int u=e[i].u,v=e[i].v; 24 int k1=find(u),k2=find(v); 25 if(k1!=k2){ 26 if(f){ 27 if(e[i].w<1){ 28 if(tot<k) tot++; 29 else continue; 30 } 31 e[i].f=1; 32 } 33 else if(!e[i].w) continue; 34 fa[k1]=k2; 35 ++cnt; 36 } 37 if(cnt+1==n) return; 38 } 39 } 40 int main() 41 { 42 scanf("%d%d%d",&n,&m,&k); 43 for(int i=1;i<=m;++i) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 44 sort(e+1,e+m+1,cmp1); 45 kruskal(0); 46 cnt=0; 47 for(int i=1;i<=n;++i){ 48 if(fa[i]==i){ 49 cnt++; 50 sign[i]=1; 51 } 52 } 53 if(cnt-1>k){ 54 puts("no solution"); 55 return 0; 56 } 57 for(int i=1;i<=m;++i) if(!e[i].w) if(sign[e[i].u]||sign[e[i].v]) e[i].w=-1; 58 sort(e+1,e+m+1,cmp2); 59 kruskal(1); 60 if(cnt+1<n||tot!=k){ 61 puts("no solution"); 62 return 0; 63 } 64 for(int i=1;i<=m;++i) if(e[i].f) printf("%d %d %d\n",e[i].u,e[i].v,e[i].w<1?0:1); 65 return 0; 66 }