三原色图(最小生成树)

传送门 HDU6349

描述

度度熊有一张 n 个点 m 条边的无向图,所有点按照 1,2,⋯,n 标号,每条边有一个正整数权值以及一种色光三原色红、绿、蓝之一的颜色。
现在度度熊想选出恰好 k 条边,满足只用这 k 条边之中的红色边和绿色边就能使 n 个点之间两两连通,或者只用这 k 条边之中的蓝色边和绿色边就能使 n 个点之间两两连通,这里两个点连通是指从一个点出发沿着边可以走到另一个点。
对于每个 k=1,2,⋯,m,你都需要帮度度熊计算选出恰好 k 条满足条件的边的权值之和的最小值。

输入

第一行包含一个正整数 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 分别表示红色、绿色和蓝色。

输出

对于每组测试数据,先输出一行信息 “Case #x:”(不含引号),其中 x 表示这是第 x 组测试数据,接下来 m 行,每行包含一个整数,第 i 行的整数表示选出恰好 i 条满足条件的边的权值之和的最小值,如果不存在合法方案,输出 −1,行末不要有多余空格。

样例

  • 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

  • output
    Case #1:
    -1
    -1
    -1
    9
    10
    12
    17
    22

题解

按两种选取颜色的方法建立两个最小生成树,然后每次分别加上剩下的边中最小的那一条,用两个数组分别保存采用两种方法得出的答案,最后比较两个数组中每个元素的大小,输出小的值

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<bits/stdc++.h>
using namespace std;
#define LL long long int
#define INIT(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define per(i,b,a) for(int i=b-1;i>=a;i--)
//b——0,-1,128,0x3f,127 ,字符
const double Pi = acos(-1);
const double E = exp(1.0);
const LL mod =1e9+7;
const int MAX=0x7fffffff;
const int MIN=-0x7fffffff;
const int INF=0x3f3f3f3f;

int f[105];
int Flag1[105],Flag2[105];
int road1[105],road2[105];

struct cun{
int a;int b;int dis;char color;
bool operator < (cun e) const{return dis<e.dis;}
void read(){scanf("%d %d %d %c",&a,&b,&dis,&color);}
}bian[1005];
void init(){for(int i=0;i<105;i++)f[i]=i;}
int find(int n){return f[n]==n?n:f[n]=find(f[n]); }
bool heb(int a,int b){int fa=find(a),fb=find(b);if(fa!=fb){f[fa]=fb;return true;}return false;}
void buildtree1(int n,int m)
{
init();
int flag=0,sum=0;
for(int i=0;i<m;i++)
{
if(bian[i].color =='B')continue;
if(heb(bian[i].a ,bian[i].b ))
{
sum+=bian[i].dis;
Flag1[i]=1;
flag++;
}
if(flag==n-1)break;
}
if(flag<n-1)INIT(road1,-1);
else{
int p=0;
for(int i=0;i<m;i++)
{
if(i<flag-1)road1[i]=-1;
else if(i==flag-1)road1[i]=sum;
else{
while(Flag1[p]==1)p++;
road1[i]=road1[i-1]+bian[p].dis ;
Flag1[p]=1;
}
}
}
}
void buildtree2(int n,int m)
{
init();
int flag=0,sum=0;
for(int i=0;i<m;i++)
{
if(bian[i].color =='R')continue;
if(heb(bian[i].a ,bian[i].b ))
{
sum+=bian[i].dis ;
Flag2[i]=1;
flag++;
}
if(flag==n-1)break;
}
if(flag<n-1)INIT(road2,-1);
else{
int p=0;
for(int i=0;i<m;i++)
{
if(i<flag-1)road2[i]=-1;
else if(i==flag-1)road2[i]=sum;
else{
while(Flag2[p]==1)p++;
road2[i]=road2[i-1]+bian[p].dis ;
Flag2[p]=1;
}
}
}
}
int main()
{
int T,n,m;
scanf("%d",&T);
for(int q=1;q<=T;q++)
{
INIT(Flag1,0);INIT(Flag2,0);
INIT(road1,0);INIT(road2,0);
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++)bian[i].read();

sort(bian,bian+m);
buildtree1(n,m);
buildtree2(n,m);

printf("Case #%d:\n",q);
for(int i=0;i<m;i++)
{
if(road1[i]==road2[i]&&road1[i]==-1)printf("-1\n");
else if(road1[i]==-1)printf("%d\n",road2[i]);
else if(road2[i]==-1)printf("%d\n",road1[i]);
else printf("%d\n",min(road1[i],road2[i]));
}
}
return 0;
}

附加彩蛋

这是百度之星资格赛中的一道题,虽然很简单,但是因为一个不起眼的越界问题,导致其他的变量被无意中修改而wa了无数发,真是菜。。。
当然,这一次也是很赚的,那就是我在看其他大佬的代码时发现了一些骚气的写法
比如:

1
2
3
4
5
6
7
struct cun{
int a;int b;int dis;char color;
bool operator < (cun e){return dis<e.dis;}
void read(){scanf("%d %d %d %c",&a,&b,&dis,&color);}
}bian[1005];
sort(bian,bian+m);
for(int i=0;i<m;i++)bian[i].read();

也就是结构体中的运算符重载(再写cmp我是狗/kb)和函数用法,以及通过结构体的构造函数赋值的方法
不得不说,这一波之后,虽然我没有变强,但是…我变骚了许多hhhhhhh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值