图论_最短路_道路与航线

此题考察了对于图的整体性质的观察和与拓扑序结合的最短路算法。

题目: 道路与航线

做法:
1、观察题目给出的图,如果去除单向边权可负的航线,图变成一块块连通块,连通块中的边都是非负且双向。

2、用拓扑排序的思想处理每个连通块,每个连通块内部用 D i j s k t r a Dijsktra Dijsktra 堆优化算法处理,如果有连到别的连通块的边,则那个连通块入度减去 1 (入度统计做为拓扑排序的入队指标)。

本题的收获:
1、精心观察图。
2、拓扑图的性质,连通块的处理。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

typedef pair<int,int> pii;

#define x first
#define y second

using namespace std;

const int M=150010,N=25010;
int n,dl,hx,s;
int idx,e[M],ver[M],head[N],ne[M];
int cnum,c[N],ind[N];
vector<int> bl[N];
int dist[N];
bool st[N];
queue<int> q;

void add(int a,int b,int c){
	e[++idx]=c;
	ver[idx]=b;
	ne[idx]=head[a];
	head[a]=idx;
}

void dfs(int s,int num){
	c[s]=num;
	bl[num].push_back(s);
	for(int i=head[s];i;i=ne[i])
		if(!c[ver[i]]) 
			dfs(ver[i],num);
}

void dij(int t){
	priority_queue<pii, vector<pii>,greater<pii> > heap;
	
	for(int i=0;i<bl[t].size();i++){
		heap.push({dist[bl[t][i]],bl[t][i]});
		//cout<<dist[bl[t][i]]<<" "<<bl[t][i]<<endl;
	}
		
		
	while(heap.size()){
		pii h=heap.top();
		heap.pop();
		
		int from=h.y;
		if(st[from]) continue;
		st[from] =true;
		
		for(int i=head[from];i;i=ne[i]){
			int val=e[i];
			int to=ver[i];
			if(c[from]!=c[to] && --ind[c[to]]==0) q.push(c[to]);
			if(dist[to]>dist[from]+val){
				dist[to]=dist[from]+val;
				if(c[to]==c[from]) heap.push({dist[to],to}); //
			}
		}
		
	}
}

void topsort(){
	memset(dist,0x3f,sizeof(dist));
	dist[s]=0;
	
	
	for(int i=1;i<=cnum;i++)
		if(c[s]==i || ind[i]==0) 
			q.push(i);
	
	while(q.size()){
		int t=q.front();
		q.pop();
		dij(t);
	}
	
			
}

int main(){
	memset(st,false,sizeof(st));
	cin>>n>>dl>>hx>>s;
	for(int i=1;i<=dl;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	for(int i=1;i<=n;i++)
		if(!c[i]){
			cnum++;
			dfs(i,cnum);
		}
		
	for(int i=1;i<=hx;i++){
		int a,b,z;
		scanf("%d%d%d",&a,&b,&z);
		add(a,b,z);
		ind[c[b]]++;
	}
	
	topsort();
	
	for(int i=1;i<=n;i++)
		if(dist[i]>0x3f3f3f3f/2) cout<<"NO PATH"<<endl;
		else cout<<dist[i]<<endl;
	
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值