CCF201703-4 地铁修建(三种做法举例)

问题描述

  A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁。
  地铁由很多段隧道组成,每段隧道连接两个交通枢纽。经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽。
  现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致。而每家公司最多只能修建一条候选隧道。所有公司同时开始施工。
  作为项目负责人,你获得了候选隧道的信息,现在你可以按自己的想法选择一部分隧道进行施工,请问修建整条地铁最少需要多少天。

输入格式

输入的第一行包含两个整数n, m,用一个空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
第2行到第m+1行,每行包含三个整数a, b, c,表示枢纽a和枢纽b之间可以修建一条隧道,需要的时间为c天。

输出格式

输出一个整数,修建整条地铁线路最少需要的天数。

样例输入

6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6

样例输出

6

样例说明

可以修建的线路有两种。
第一种经过的枢纽依次为1, 2, 3, 6,所需要的时间分别是4, 4, 7,则整条地铁线需要7天修完;
第二种经过的枢纽依次为1, 4, 5, 6,所需要的时间分别是2, 5, 6,则整条地铁线需要6天修完。
第二种方案所用的天数更少。

评测用例规模与约定

对于20%的评测用例,1 ≤ n ≤ 10,1 ≤ m ≤ 20;
对于40%的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000;
对于60%的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000,1 ≤ c ≤ 1000;
对于80%的评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000;
对于100%的评测用例,1 ≤ n ≤ 100000,1 ≤ m ≤ 200000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000。
所有评测用例保证在所有候选隧道都修通时1号枢纽可以通过隧道到达其他所有枢纽。

思路

  • 由于施工公司的数量为 n n n,所以不会出现某隧道等待施工的现象,故该题就是求解从起点到终点所经过的边权最大值的最小值,可选用 D i j k s t r a Dijkstra Dijkstra 算法求解,设 d i s [ x ] dis[x] dis[x] 表示 1 1 1 x x x 边权最大值的最小值,则只需要将松弛条件改为 d i s [ y ] > m a x ( d i s [ x ] , w ) dis[y]>max(dis[x],w) dis[y]>max(dis[x],w) 即可。
  • 或者用最小生成树做,1 和 n 在一个连通分支里就退出即可。

代码实现

#include <bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxm=400010;
const int inf=1e9;

int n,m,u,v,w,tot,head[maxn],dis[maxn];
bool vis[maxn];
struct Edge{
	int to,next,w;
}edge[maxm];
struct heap{
	int s,w;
	heap(int ss,int ww):s(ss),w(ww){}
	bool operator<(const heap h)const{return w>h.w;}
};

void add(int x,int y,int w){
	edge[++tot].to=y;
	edge[tot].w=w;
	edge[tot].next=head[x];
	head[x]=tot;
}

int main()
{
	scanf("%d%d",&n,&m);
	tot=0;
	for(int i=1;i<=n;i++){
		head[i]=-1;
		vis[i]=0;
		dis[i]=inf;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}	
	priority_queue<heap> q;
	dis[1]=0;
	q.push(heap(1,0));
	while(!q.empty()){
		int x=q.top().s;
		q.pop();
		if(vis[x])
			continue;
		vis[x]=1;
		for(int i=head[x];i!=-1;i=edge[i].next){
			int y=edge[i].to,w=edge[i].w;
			if(dis[y]>max(w,dis[x])){
				dis[y]=max(w,dis[x]);
				q.push(heap(y,dis[y]));
			}
		}
	}
	printf("%d\n",dis[n]);
	return 0;
} 
#include <bits/stdc++.h>
using namespace std;
const int maxm=400010;
const int maxn=100010;

int n,m,a,b,c,idx=0,f[maxn];
struct Edge{
	int from,to,dis;
	Edge(){}
	Edge(int ff,int tt,int dd):from(ff),to(tt),dis(dd){}
	bool operator<(const Edge &e)const{return dis<e.dis;}
}edge[maxm];

int father(int x){
	while(x!=f[x]){
		f[x]=f[f[x]];
		x=f[x];
	}
	return x;
}

void unionfather(int x,int y){
	f[father(y)]=father(x);
}

void Kruskal(){
	for(int i=1;i<=n;i++)
		f[i]=i;
	sort(edge,edge+idx);
	int ans=0;
	for(int i=0;i<idx;i++){
		if(father(1)==father(n))
			break;
		if(father(edge[i].from)!=father(edge[i].to)){
			unionfather(edge[i].from,edge[i].to);
			ans=max(ans,edge[i].dis);
		}
	}
	printf("%d\n",ans);
} 

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		edge[idx++]=Edge(a,b,c);
		edge[idx++]=Edge(b,a,c);
	}
	Kruskal();
	return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int maxn=100010;
const int maxm=400010;

struct Edge{
	int u,v,w,next;
}edge[maxm];

struct node{
	int from,to,dis;
	node(int ff,int tt,int dd):from(ff),to(tt),dis(dd){}
	bool operator<(const node &s)const{return dis>s.dis;}
};

int n,m,tot,a,b,c,head[maxn];
bool vis[maxn];

void init(int nn){
	tot=0;
	for(int i=1;i<=nn;i++){
		vis[i]=false;
		head[i]=-1;
	}
}

void add(int x,int y,int w){
	edge[++tot].u=x;
	edge[tot].v=y;
	edge[tot].w=w;
	edge[tot].next=head[x];
	head[x]=tot;
}

priority_queue<node> q;

int Prim(int s){
	int ans=0;
	vis[s]=1;
	int cnt=1;
	while(cnt<=n-1){
		for(int i=head[s];i!=-1;i=edge[i].next){
			if(!vis[edge[i].v]){
				q.push(node(edge[i].u,edge[i].v,edge[i].w));
			}
		}
		while(!q.empty()&&vis[q.top().to])
			q.pop();
		if(q.empty())
			break;
		node tmp=q.top();
		s=tmp.to;
		vis[s]=true;
		ans=max(ans,tmp.dis);
		if(s==n)
			break;
		q.pop();
		cnt++;
	}
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	init(n);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	printf("%d\n",Prim(1));
	return 0;
}

空间上:Kruskal 优于 Dijsktra 优于 Prim
时间上差不多

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是第一段代码的解答: ccf201703-2是一个关于学生移位操作的问题。代码中使用了结构体来存储学生的编和他在队伍中的位置,并通过重载运算符来对学生元素按照位置进行排序。首先,输入学生的数量n,并初始化一个vector来存储学生的信息。然后,根据学生的数量循环输入每个学生的编和初始位置,并将其添加到vector中。接下来,输入移位操作的次数m,并循环输入每个移位操作的目标学生编和移动的位置。根据目标学生编,找到对应的学生在vector中的位置,并将其位置更新为目标位置。如果目标位置大于0,则将其前面的学生位置都减1;如果目标位置小于0,则将其后面的学生位置都加1。最后,对vector进行排序,并输出排序后的学生编。 请注意,这段代码中使用了C++的一些语法和库函数,需要在编译时包含相关的头文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [CCF:201703-2 学生排队](https://blog.csdn.net/weixin_42729072/article/details/105435070)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [第十次ccf 学生排队](https://blog.csdn.net/xiaoli_nu/article/details/65468043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [CCF-CSP 201703-2学生排队 一维数组满分题解](https://blog.csdn.net/m0_53641110/article/details/121446439)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值