【题解】洛谷P2323 公路修建问题(生成树)

9 篇文章 0 订阅
2 篇文章 0 订阅

概括一下 就是最小生成树问题对于连接两个点的一条边都可以选择两个权值(一级 二级),一级权值>=二级,要求必须选至少k条一级公路(因为一级权值始终比二级全职大,所以我们就选k条一级公路),求这样构建出来的最小生成树最长的一条大小与选择第几条公路和级别。

这里我们需要写三个排序函数,第一个按照一级公路权值由小到大排序,选出k条公路。第二个按照二级公路权值由小到大排序,选出n-1-k条公路,记录下来答案需要的值后最后进行第三次排序,按照公路序号由小到大排,输出结果。注意这里枚举的是每一条边后在判断各种条件....之前疯狂WA,还要注意题目给的是m-1条公路,以及调用快排函数时的各种范围(.....)

 

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=10010;
const int maxm=50010;
int n,m,k;
int ans=-100;
int fa[maxn];
struct edge{
	int u,v,w1,w2;
	int t;
}e[maxm*2];
struct print{
	int first;
	int second;
}qwq[maxm*2];
int find(int x)
{
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
bool cmp1(const edge &a,const edge &b)
{
	if(a.w1==b.w1) return a.w2>b.w2;
	return a.w1<b.w1;
}
bool cmp2(const edge &a,const edge &b)
{
	return a.w2<b.w2;
}
void kruskal()
{
	int now=0;
	for(int i=1;i<=m;i++)
	{
		int f1=find(e[i].u);
		int f2=find(e[i].v);
		if(f1!=f2)
		{
			fa[f1]=fa[f2];
			ans=max(ans,e[i].w1);
			qwq[now].first=e[i].t;
			qwq[now].second=1;
			now++;
			if(now==k) break;
		}
	}
	sort(e+1,e+m,cmp2);
	for(int i=1;i<=m;i++)
	{
		int f1=find(e[i].u);
		int f2=find(e[i].v);
		if(f1!=f2)
		{
			fa[f1]=fa[f2];
			ans=max(ans,e[i].w2);
			qwq[now].first=e[i].t;
			qwq[now].second=2;	
			now++;
			if(now==n-1) break;
		}
	}
}
bool cmp3(const print &a,const print &b)
{
	return a.first<b.first;
}
int main()
{
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=m-1;i++)
	{
		scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w1,&e[i].w2);
		e[i].t=i;
	}
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
	}
	sort(e+1,e+m,cmp1);
	kruskal();
	sort(qwq,qwq+n-1,cmp3);
	printf("%d\n",ans);
	for(int i=0;i<=n-2;i++)
	{
		printf("%d %d\n",qwq[i].first,qwq[i].second);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值