2022 Winter Individual Contest - C

C - Crowd Control

大意:给你个图,让你找一个从起点到终点,最小边的最大值的路径,然后把与这些路径上的点相连的非路径边给删除掉,然后输出这些边的编号;

  1. 怎么找这个最小边的最大值?
    当前这个点到起点的所有路径中,每一条路径上都有最小边值,
    把这条边和其他路径上的最小边比较留最大的,即最小边最大化;
  2. 具体怎么实现?
    设dis[u]为起点到u点的最小边最大值,w[i]为点u,v的边权
    从起点到u,再到v的这条路的最小边最大值为:dis[v]=min(dis[u],w[i]);
    但能到点v的不止uv这条边,所以dis[v]=max(dis[v],min(dis[u],w[i]);
  3. 为什么 dis[v]=min(dis[u],w[i])
    (1). 假如w[i]>dis[u],即它比从起点到u的每一条路径上的最小边都大
    所以它都没资格成为某条路径上的最小边,没法和dis[u]竞争,所以选小的dis[u].
    (2).假如w[i]<dis[u],即他现在成为最小边比他大的路径新的最小边了,
    他把比他大的都换下去了,他不就成为现在最大的了嘛,所以选小的w[i];
  4. 用spfa的思想,进行缩边,并储存路径
  5. 根据题目要求删边然后输出
#include<bits/stdc++.h>
using namespace std;

const int N=1e3+10,M=2*N,INF=0x3f3f3f3f;
int n,m;
int head[N],ne[M],ue[M],ve[M],w[M],cnt;

queue<int> qu;	//spfa所用的队列 
bool vis[N];	//判断是否在队列 
int pre[N];   	//记录路径 
int dis[N];     //不再表示最短路,而是 从起点到i点的最小边的最大值 

queue<int> ans;


void add(int a,int b,int c){
	ue[cnt]=a;
	ve[cnt]=b;
	w[cnt]=c;
	ne[cnt]=head[a];
	head[a]=cnt++;
}


void spfa(){
	qu.push(0);
	vis[0]=1;
	dis[0]=INF;//初始化为最大值,才能更新某一条路径的最小边; 
	int u,v;
	while(!qu.empty()){
		u=qu.front();
		qu.pop();
		vis[u]=0; 
		for(int i=head[u];i!=-1;i=ne[i]){
			v=ve[i];
			// dis[v]=max(dis[v],min(dis[u],w[i]);
			if(dis[v]<min(dis[u],w[i])){ 
				dis[v]=min(dis[u],w[i]);
				pre[v]=u;
				if(vis[v]==0){
					vis[v]=1;
					qu.push(v);
				}
			}
		}
	}
}

void init(){
	for(int i=0;i<n;i++) head[i]=-1,pre[i]=-1;
}

int main(){
	cin>>n>>m;
	init();
	
	//因为每个边都有编号,所以用数组模拟邻接矩阵,能直接通过下标访问
	//又因为图是无向图,所以边的编号是下标除二;
	int a,b,c;
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;
		add(a,b,c);
		add(b,a,c);
	}
	
	spfa();//spfa变形找最小边的最大值,并记录路径 
	
	
	for(int e=n-1;e!=-1;e=pre[e]){
		vis[e]=1;
		if(e==0) break;
	}
	for(int i=0;i<cnt;i+=2){
		if(vis[ue[i]]==1&&vis[ve[i]]==0||vis[ue[i]]==0&&vis[ve[i]]==1){
			ans.push(i/2);
		}
		else if(vis[ue[i]]==1&&vis[ve[i]]==1&&pre[ue[i]]!=ve[i]&&pre[ve[i]]!=ue[i]){
			ans.push(i/2);
		}
	}
	if(ans.empty()) cout<<"none";
	while(!ans.empty()){
		cout<<ans.front()<<" ";
		ans.pop();
	}
	return 0;
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值