三原色图
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 140 Accepted Submission(s): 47
Problem Description
度度熊有一张 n 个点 m 条边的无向图,所有点按照 1,2,⋯,n 标号,每条边有一个正整数权值以及一种色光三原色红、绿、蓝之一的颜色。
现在度度熊想选出恰好 k 条边,满足只用这 k 条边之中的红色边和绿色边就能使 n 个点之间两两连通,或者只用这 k 条边之中的蓝色边和绿色边就能使 n个点之间两两连通,这里两个点连通是指从一个点出发沿着边可以走到另一个点。
对于每个 k=1,2,⋯,m,你都需要帮度度熊计算选出恰好 k 条满足条件的边的权值之和的最小值。
Input
第一行包含一个正整数 T,表示有 T 组测试数据。
接下来依次描述 T 组测试数据。对于每组测试数据:
第一行包含两个整数 n 和 m,表示图的点数和边数。
接下来 m 行,每行包含三个整数 a,b,w 和一个字符 c,表示有一条连接点 a 与点 b 的权值为 w、颜色为 c 的无向边。
保证 1≤T≤100,1≤n,m≤100,1≤a,b≤n,1≤w≤1000,c∈{R,G,B},这里 R,G,B 分别表示红色、绿色和蓝色。
Output
对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 x 组测试数据,接下来 m 行,每行包含一个整数,第 i 行的整数表示选出恰好 i 条满足条件的边的权值之和的最小值,如果不存在合法方案,输出 −1,行末不要有多余空格。
Sample Input
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
Sample Output
Case #1: -1 -1 -1 9 10 12 17 22
中文题题意略。
思路:求两个最小生成树,然后依次把未在生成树中的边权值从小到大加即可。刚开始我预处理没处理好,一直WA。后来把优先队列放到求最小生成树的函数里面就对了。
代码:
#include<bits/stdc++.h>
#define ull unsigned long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=210;
int par[maxn],rk[maxn];
priority_queue<int,vector<int>,greater<int> >q;
int c[maxn];
struct node
{
int u,v,w;
int col;
bool operator<(node a)const{
return w<a.w;
}
}a[maxn];
void init(int n)
{
for(int _=0;_<=n;_++)
{
rk[_]=0;
par[_]=_;
}
}
int find(int x){
int root=x;
while(root!=par[root]) root=par[root];
while(x!=root){
int t=par[x];
par[x]=root;
x=t;
}
return root;
}
void un(int x,int y)
{
x=find(x);
y=find(y);
if(rk[x]<rk[y]){
par[x]=y;
}
else{
par[y]=x;
if(rk[x]==rk[y]) rk[x]++;
}
}
void kruskal(int n,int m,int ch)
{
int ng=0,res=0;
sort(a,a+m);
while(!q.empty())q.pop();
for(int i=0;i<m;i++)
if(a[i].col!=ch&&ng<n-1){
if(find(a[i].u)!=find(a[i].v)){
un(a[i].u,a[i].v);
res+=a[i].w;
ng++;
}
else q.push(a[i].w);
}
else q.push(a[i].w);
if(ng<n-1) {res=-1;return;}
int k=n-1;
if(c[k]==-1) c[k]=res;
else c[k]=min(c[k],res);
while(!q.empty())
{
res+=q.top();
q.pop();
k++;
if(c[k]==-1) c[k]=res;
else c[k]=min(c[k],res);
}
}
int main()
{
int n,m,s,T,cas=1;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&m);
init(n);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
char ch;
cin>>ch;
if(ch=='B')a[i].col=0;
else if(ch=='R')a[i].col=1;
else if(ch=='G')a[i].col=2;
}
memset(c,-1,sizeof(c));
kruskal(n,m,0);
init(n);
kruskal(n,m,1);
printf("Case #%d:\n",cas++);
for(int i=1;i<=m;i++)
printf("%d\n",c[i]);
}
return 0;
}