百度之星程序设计大赛资格赛 1006三原色图(最小生成树)

本文详细解析了一道涉及图论的编程竞赛题目,通过构造两次最小生成树算法,解决了一个复杂的边选择问题,旨在帮助读者理解如何在特定条件下寻找最优解。

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

度度熊有一张 nn 个点 mm 条边的无向图,所有点按照 1,2,\cdots,n1,2,⋯,n 标号,每条边有一个正整数权值以及一种色光三原色红、绿、蓝之一的颜色。

现在度度熊想选出恰好 kk 条边,满足只用这 kk 条边之中的红色边和绿色边就能使 nn 个点之间两两连通,或者只用这 kk 条边之中的蓝色边和绿色边就能使 nn 个点之间两两连通,这里两个点连通是指从一个点出发沿着边可以走到另一个点。

对于每个 k=1,2,\cdots,mk=1,2,⋯,m,你都需要帮度度熊计算选出恰好 kk 条满足条件的边的权值之和的最小值。

Input

第一行包含一个正整数 TT,表示有 TT 组测试数据。

接下来依次描述 TT 组测试数据。对于每组测试数据:

第一行包含两个整数 nn 和 mm,表示图的点数和边数。

接下来 mm 行,每行包含三个整数 a,b,wa,b,w 和一个字符 cc,表示有一条连接点 aa 与点 bb 的权值为 ww、颜色为 cc 的无向边。

保证 1 \leq T \leq 1001≤T≤100,1 \leq n,m \leq 1001≤n,m≤100,1 \leq a,b \leq n1≤a,b≤n,1 \leq w \leq 10001≤w≤1000,c \in {R,G,B}c∈{R,G,B},这里 R,G,BR,G,B 分别表示红色、绿色和蓝色。

Output

对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 xx 组测试数据,接下来 mm 行,每行包含一个整数,第 ii 行的整数表示选出恰好 ii 条满足条件的边的权值之和的最小值,如果不存在合法方案,输出 -1−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

题意:中文,就多说了

思路:这题一看就是求两次最小生成树,进行比较,说一下,我在做题时遇到的困惑吧,当用c++进行调用函数时,调用函数中存在地址,这时候你要是用memset初始化时,一定要注意。当结构体类型中包含指针时,在使用memset初始化时需要小心。当memset初始化时,并不会初始化p_x指向的int数组单元的值,而会把已经分配过内存的p_x指针本身设置为0,造成内存泄漏。同理,对std::vector等数据类型,显而易见也是不应该使用memset来初始化的。 

代码:

#include<bits/stdc++.h>
#include<string.h>
using namespace std;
#define Max 110
struct node
{
	int u,v,w;
	char c;
}s1[Max];
int f[Max];
int bk1[Max],bk2[Max];
int ans1[Max],ans2[Max];
int n,m;
bool cmp(node a,node b)
{
	return a.w<b.w;
}

int find(int u)
{
	if(u==f[u])
		return u;
	else
		return f[u] = find(f[u]);
}
void sss(char a,char b,int ans[],int book[])
{
	//memset(book,0,sizeof(book));
	//当调用函数中存在指针时,不会初始化指针指向的int数组单元,
	//而是把已经分配过内存的指针本身初始为0,所以book数组不会初始化成功; 
	for(int i = 0;i<=m;i++)
		book[i] = 0;
	for(int i = 1;i <= n;i++)
		f[i] = i;
	int num = 0,sum = 0;
	for(int i = 1;i<=m;i++)
	{
		if(s1[i].c==a||s1[i].c==b)
		{
			int t1 = find(s1[i].u);
			int t2 = find(s1[i].v);
			if(t1 != t2)
			{
				num++;
				if(t1<t2)
					f[t1] = t2;
				else f[t2] = t1;
				book[i] = 1;
				sum += s1[i].w;	
			}
			if(num==n-1) break;
		}
	}
	if(num!=n-1) return ;
	else
	{
		ans[num] = sum;
		for(int i = 1;i<=m;i++)
		{
			if(!book[i])
			{
				num++;
				sum += s1[i].w;
				ans[num] = sum;
				book[i] = 1;
			}
		}
		return ;
	}
}
int main()
{
	int t,num = 1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i = 1;i<=m;i++)
			scanf("%d%d%d %c",&s1[i].u,&s1[i].v,&s1[i].w,&s1[i].c);
		sort(s1+1,s1+m+1,cmp);
		memset(ans1,-1,sizeof(ans1));
		memset(ans2,-1,sizeof(ans2));
		
		sss('R','G',ans1,bk1);
		sss('B','G',ans2,bk2);
		printf("Case #%d:\n",num++);
		for(int i = 1;i<=m;i++)
		{
			if(ans1[i]==-1&&ans2[i]==-1)
				printf("-1\n");
			else if(ans1[i]!=-1&&ans2[i]!=-1)
				printf("%d\n",min(ans1[i],ans2[i]));
			else if(ans1[i]!=-1)
				printf("%d\n",ans1[i]);
			else printf("%d\n",ans2[i]);
		}
	}
	return 0;
} 

都是用一个数组存的代码二:

#include<bits/stdc++.h>
#include<string.h>
using namespace std;
#define Max 110
struct node
{
	int u,v,w;
	char c;
}s1[Max];
int f[Max];
int book[Max];
int ans[Max];
int n,m;
bool cmp(node a,node b)
{
	return a.w<b.w;
}

int find(int u)
{
	if(u==f[u])
		return u;
	else
		return f[u] = find(f[u]);
}
void sss(char a,char b)
{
	memset(book,0,sizeof(book)); 
	for(int i = 1;i <= n;i++)
		f[i] = i;
	int num = 0,sum = 0;
	for(int i = 1;i<=m;i++)
	{
		if(s1[i].c==a||s1[i].c==b)
		{
			int t1 = find(s1[i].u);
			int t2 = find(s1[i].v);
			if(t1 != t2)
			{
				num++;
				if(t1<t2)
					f[t1] = t2;
				else f[t2] = t1;
				book[i] = 1;
				sum += s1[i].w;	
			}
			if(num==n-1) break;
		}
	}
	if(num!=n-1) return ;
	else
	{
		if(ans[num]==-1)
			ans[num] = sum;
		else ans[num] = min(ans[num],sum);
		for(int i = 1;i<=m;i++)
		{
			if(!book[i])
			{
				num++;
				sum += s1[i].w;
				if(ans[num]==-1) ans[num] = sum;
				else ans[num] = min(ans[num],sum); 
				book[i] = 1;
			}
		}
		return ;
	}
}
int main()
{
	int t,num = 1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i = 1;i<=m;i++)
			scanf("%d%d%d %c",&s1[i].u,&s1[i].v,&s1[i].w,&s1[i].c);
		sort(s1+1,s1+m+1,cmp);
		memset(ans,-1,sizeof(ans));
		sss('R','G');
		sss('B','G');
		printf("Case #%d:\n",num++);
		for(int i = 1;i<=m;i++)
			printf("%d\n",ans[i]);
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值