2018 百度之星 1006 最小生成树
http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=820&pid=1006
度度熊有一张 nnn 个点 mmm 条边的无向图,所有点按照 1,2,⋯,n1,2,\cdots,n1,2,⋯,n 标号,每条边有一个正整数权值以及一种色光三原色红、绿、蓝之一的颜色。
现在度度熊想选出恰好 kkk 条边,满足只用这 kkk 条边之中的红色边和绿色边就能使 nnn 个点之间两两连通,或者只用这 kkk 条边之中的蓝色边和绿色边就能使 nnn 个点之间两两连通,这里两个点连通是指从一个点出发沿着边可以走到另一个点。
对于每个 k=1,2,⋯,mk=1,2,\cdots,mk=1,2,⋯,m,你都需要帮度度熊计算选出恰好 kkk 条满足条件的边的权值之和的最小值。
第一行包含一个正整数 TTT,表示有 TTT 组测试数据。
接下来依次描述 TTT 组测试数据。对于每组测试数据:
第一行包含两个整数 nnn 和 mmm,表示图的点数和边数。
接下来 mmm 行,每行包含三个整数 a,b,wa,b,wa,b,w 和一个字符 ccc,表示有一条连接点 aaa 与点 bbb 的权值为 www、颜色为 ccc 的无向边。
保证 1≤T≤1001 \leq T \leq 1001≤T≤100,1≤n,m≤1001 \leq n,m \leq 1001≤n,m≤100,1≤a,b≤n1 \leq a,b \leq n1≤a,b≤n,1≤w≤10001 \leq w \leq 10001≤w≤1000,c∈{R,G,B}c \in {R,G,B}c∈{R,G,B},这里 R,G,BR,G,BR,G,B 分别表示红色、绿色和蓝色。
对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 xxx 组测试数据,接下来 mmm 行,每行包含一个整数,第 iii 行的整数表示选出恰好 iii 条满足条件的边的权值之和的最小值,如果不存在合法方案,输出 −1-1−1,行末不要有多余空格。
1 5 8 1 5 1 R 2 1 2 R 5 4 5 R 4 5 3 G 1 3 3 G 4 3 5 G 5 4 1 B 1 2 2 B
Case #1: -1 -1 -1 9 10 12 17 22
题意:中文题不解释
思路:建立两个图(红绿和蓝绿)开始求各自最小生成树 小于n-1条边输出-1(因为无法全部遍历) 等于n-1输出两个图跑最小生成树的最小值 大于n-1输出两个图分别跑最小生成树后剩下的边从小到大加
进最小生成树的边然后还是要比较输出两个图的最小值(这个点没考虑到导致wa了n遍,,,说多了都是泪
代码(很多不必要的操作,但是只是为了AC):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<set> #include<algorithm> #include<map> #define maxn 50010 using namespace std; int par[maxn]; int ranke[maxn]; int vis[maxn],vis2[maxn]; int used1[maxn],used2[maxn]; int op1[maxn],op2[maxn]; void init(int n) { for(int i=0;i<n;i++){ par[i]=i; ranke[i]=0; } } int find(int x)//找父亲 { if(par[x]==x)return x; else { return par[x]=find(par[x]);//递归找父亲(因为父亲是统一的) } } void unite(int x,int y) { x=find(x); y=find(y); if(x==y)return ; if(ranke[x]<ranke[y]) { par[x]=y; } else par[y]=x; if(ranke[x]==ranke[y])ranke[x]++; } bool same(int x,int y) { return find(x)==find(y); } struct edge{ int from,to,cost; }; bool comp(const edge& e1,const edge& e2) { return e1.cost<e2.cost; } edge mp[maxn],mp2[maxn]; int V,E,k1,k2; int res,res2; int flag,flag2; char col,cha; int kruskal() { flag=1; memset(used1,0,sizeof(used1)); sort(mp,mp+k1,comp); init(V); res=0; for(int i=0;i<k1;i++) { edge s=mp[i]; //cout<<s.from<<s.to<<endl; if(!same(s.from,s.to)){ unite(s.from,s.to); flag++; res+=s.cost; used1[s.cost]++; } } return res; } int kruskal2() { flag2=1; memset(used2,0,sizeof(used2)); sort(mp2,mp2+k2,comp); init(V); res2=0; for(int i=0;i<k2;i++) { edge s=mp2[i]; if(!same(s.from,s.to)){ unite(s.from,s.to); flag2++; res2+=s.cost; used2[s.cost]++; } } return res2; } int main() { int t; cin>>t; for(int z=1;z<=t;z++){ int d1,d2,dis; int mon; cin>>V>>E; k1=0; k2=0; for(int i=0,j=0,jj=0;i<E;i++) { cin>>d1>>d2>>dis; scanf("%c%c",&cha,&col); vis[j++]=dis; vis2[jj++]=dis; d1--; d2--; if(col=='G'||col=='B') { mp2[k2].from=d1; mp2[k2].to=d2; mp2[k2].cost=dis; k2++; } if(col=='G'||col=='R'){ mp[k1].from=d1; mp[k1].to=d2; mp[k1].cost=dis; k1++; } } //cout<<k2<<endl; cout<<"Case #"<<z<<":"<<endl; if(V==1){ sort(vis,vis+E); int sum=0; for(int i=0;i<E;i++) { sum+=vis[i]; cout<<sum<<endl; } } else { int ans1=kruskal(); int ans2=kruskal2(); //cout<<ans1<<endl; //cout<<flag2<<endl; int oo=0; if(flag!=V&&flag2!=V) { for(int i=0;i<E;i++){cout<<"-1"<<endl;oo=1;} } else if(flag!=V) ans1=1000000000; else if(flag2!=V) ans2=1000000000; int num=0; if(oo==1)continue; for(int x=0;x<V-1-1;x++){ op1[x]=-1; op2[x]=-1; } op1[V-2]=ans1; op2[V-2]=ans2; sort(vis,vis+E); for(int i=0;i<E;i++) { if(used1[vis[i]]!=0){used1[vis[i]]--;vis[i]=1000000000;} } sort(vis,vis+E); int m=0; for(int ss=V-1;ss<E;ss++) { op1[ss]=op1[ss-1]+vis[m++]; } sort(vis2,vis2+E); for(int i=0;i<E;i++) { // cout<<used2[vis[i]]; if(used2[vis2[i]]!=0){used2[vis2[i]]--;vis2[i]=1000000;} } sort(vis2,vis2+E); // for(int i=0;i<E;i++)cout<<vis[i]<<endl; m=0; for(int ss=V-1;ss<E;ss++) { op2[ss]=op2[ss-1]+vis2[m++]; } for(int i=0;i<E;i++) cout<<min(op1[i],op2[i])<<endl; } // if(flag!=V)cout<<"No"<<endl; // else // { // if(res>mon)cout<<"No"<<endl; // else cout<<"Yes"<<endl; // } } return 0; }