HDU 6349 ( 三原色图 )(两个最小生成树)

本文介绍了一道关于寻找特定条件下最小生成树的问题。题目要求对于一张包含红色、绿色和蓝色边的图,在不同数量的边下找到使得图两两连通的最小权值之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

三原色图

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值