最小生成树(两次)简易处理 HDOJ6349

                            三原色图

Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 524 Accepted Submission(s): 177

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

解题思路:这个题本身就是一个考察kruskal算法的一个题,首先要了解kruskal算法,这种算法,网上有很多模板,我也写过类似文章,不再多说,这个题的关键就是不是建立两个并查集,而是一个并查集进行两遍。还有注意始终不符合条件的情况

#include<bits/stdc++.h>
#define ull unsigned long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=210;
int par[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 i=0;i<=n;i++)
    {
        par[i]=i;
    }
}

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);
     par[x]=y;
}

void kruskal(int n,int m,int ch)
{
    int ng=0,res=0;//res是最优解,ng是建立的树的边(树的边永远比点 少一);
    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;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值