最短路练习-Floyd-SPFA-Dijkstra

Border Restrictions

Description
To prevent the spread of the virus, many countries have closed their borders to travellers arriving from certain other countries. Over time, different people travel to different countries and it is still possible for the virus to spread from country to country. If the virus starts in one country, how long will it be before it has spread to other countries? We will assume that if the virus is in one country in week i and a second country allows travellers from the first country, the virus will reach the second country in week i+1. Once the virus reaches a country, it is there forever, until a vaccine is found.
Input
The first line of input contains N, the number of countries in the world, with 1≤N≤300. N lines of input follow, each describing a country. Each line has the form DESTINATION allows travellers from ORIGIN1 ORIGIN2 ORIGIN3, where DESTINATION, ORIGIN1, ORIGIN2, ORIGIN3 are names of countries consisting of at most 30 uppercase letters from A to Z. Every country has a unique name. Note that there may be as few as 0 and as many as N−1 origin countries on a line, not always three. Also, the origin countries on each line are distinct and do not include the destination country. In week one, the virus is only in the first country listed in the input.
Output
Output N lines, one for each country, sorted in alphabetical order. On each line, output the country name followed by the number of the week in which the virus reaches that country. If the virus can never reach some country, output 0 instead of the number of the week in which the virus reaches that country.
Samples
Input 复制
3
CANADA allows travellers from USA
MEXICO allows travellers from USA
USA allows travellers from CANADA MEXICO
Output
1
3
2
Source
Waterloo Fall 2020 local

题意:输入n表示有n座城市,接下来有n行,格式为:A allows travellers from B 表示B城市可以通往A城市;病毒先在第一行第一个城市爆发;爆发病毒的城市可以传染给此城市通向的城市,被传染的城市在第二周会爆发病毒,问你每个城市会在第几周爆发病毒,若不会爆发病毒则输出0;

这题有很多解法:
①Floyd
②Dijkstra(朴素)
③SPFA (朴素bfs)
虽然算法效率都不高,但是数据范围都很小,所以还是可以顺利AC的


Code1: Floyd

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e9;
map<string,ll>m1;
int a[330][330];
string s,t,temp;
int n,num;
int main() {
	cin>>n;
	getchar();
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			a[i][j]=inf;
	for(int i=1; i<=n; i++) {
		int tt=0;
		getline(cin,s);
		istringstream ss(s);
		while(ss>>t) {
			tt++;
			if(tt==2||tt==3||tt==4)	continue;
			if(!m1.count(t))	m1[t]=++num;
			if(tt==1)	temp=t;
			else	a[m1[t]][m1[temp]]=1;
		}
	}
	a[1][1]=0;
	for(int k=1; k<=num; k++)
		for(int j=1; j<=num; j++)
			for(int i=1; i<=num; i++)
				if(a[j][i]>a[j][k]+a[k][i])
					a[j][i]=a[j][k]+a[k][i];

	for(map<string,ll>::iterator iter=m1.begin(); iter!=m1.end(); iter++) {
		if(a[1][iter->second]>=inf)	cout<<"0";
		else	cout<<a[1][iter->second]+1;
		cout<<"\n";
	}
}

Code2:Dijkstra(朴素)

#include<bits/stdc++.h>
#pragma GCC optimise(2)
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int inf = 1e9;
int n;
map<string,int> mp;
vector<int> v[330];
bool vis[330];
int dis[330];
void Dijkstra(int n) {
	dis[1] = 1; //起点为1号点 
	for(int i=1; i<=n; i++) {
		int u = 0;
		int maxx = inf;
		for(int j=1; j<=n; j++) {
			if(!vis[j] && dis[j]<maxx) { //每次找离出发点最近的点
				maxx = dis[j];
				u = j;
			}
		}
		if(!u) break;
		vis[u] = 1;
		for(int j=0; j<v[u].size(); j++) {
			int k = v[u][j];
			if(dis[k] > dis[u] + 1) dis[k] = dis[u] + 1;
		}
	}
}
int main() {
	cin>>n;
	getchar();
	for(int i=1; i<=n+1; i++) dis[i] = inf;
	int cnt = 0;
	for(int i=0; i<n; i++) {
		string s,temp,h;
		int k = 0;
		getline(cin,s);
		istringstream ss(s);
		while(ss>>temp) {
			k++;
			if(k==2||k==3||k==4) continue;
			if(!mp[temp]) mp[temp] = ++cnt;
			if(k==1) h = temp;
			else v[mp[temp]].push_back(mp[h]);
		}
	}
	Dijkstra(n);
	map<string,int>::iterator it;
	for(it = mp.begin(); it!=mp.end(); it++) {
		if(dis[it->second]>=inf) cout<<"0";
		else cout<<dis[it->second];
		putchar('\n');
	}
	return 0;
}

Code3:SPFA(vis数组判断该点是否在队列中)

#include<bits/stdc++.h>
#pragma GCC optimise(2)
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int inf = 1e9;
int n;
map<string,int> mp;
vector<int> v[330];
bool vis[330];
int dis[330];
int main() {
	cin>>n;
	getchar();
	for(int i=1; i<=n+1; i++) dis[i] = inf;
	int cnt = 0;
	for(int i=0; i<n; i++) {
		string s,temp,h;
		int k = 0;
		getline(cin,s);
		istringstream ss(s);
		while(ss>>temp) {
			k++;
			if(k==2||k==3||k==4) continue;
			if(!mp[temp]) mp[temp] = ++cnt;
			if(k==1) h = temp;
			else {
				v[mp[temp]].push_back(mp[h]);
				//cout<<h<<" "<<mp[h]<<" "<<temp<<" "<<mp[temp]<<endl;
			}

		}
	}
	dis[1] = 1,vis[1] = 1;
	queue<int> Q;
	Q.push(1);
	while(Q.size()) {
		int now = Q.front();
		Q.pop();
		for(int i=0; i<v[now].size(); i++) {
			int m = v[now][i];
			if(!vis[m]) {
				vis[m] = 1;
				Q.push(m);
			} else continue;
			if(dis[m] > dis[now] +1) {
				dis[m] = dis[now] + 1;
			}
		}
	}
	map<string,int>::iterator it;
	for(it = mp.begin(); it!=mp.end(); it++) {
		if(dis[it->second]>=inf) cout<<"0";
		else cout<<dis[it->second];
		putchar('\n');
	}
	return 0;
}

这里spfa有点问题,就是只有在该点被松弛的情况下才判断该点是否在队列中,要不会有没必要的点加入到队列中,增加复杂度;
此点被松弛了,说明离起点的距离被更新,所以需要将他通向的点也更新,即渐渐逼近的思想。

for(int i=0; i<v[now].size(); i++) {
	int m = v[now][i];
	if(dis[m] > dis[now] +1) {
		dis[m] = dis[now] + 1;
		if(!vis[m]) {
			vis[m] = 1;
			Q.push(m);
		} 
	}
}

堆优化Dijkstra: 例题.
Code:

#include<bits/stdc++.h>
#pragma GCC optimise(2)
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const ll inf = 1e14+10;
inline int read() {
	int x=1,f=0; char ch;ch = getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') x=-1;ch = getchar();	}
	while(ch>='0' && ch<='9') {f = f*10+ch-'0';ch = getchar();	}
	return x*f;
}
int n,m,s;
ll d[N];
bool vis[N];
vector<pair<int,int> > v[N];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > Q;
void Dijkstra() {
	for(int i=1;i<=n;i++) d[i] = inf;
	d[1] = 0;
	Q.push({0,1});
	while(Q.size()) {
		int u = Q.top().second;
		Q.pop();
		if(vis[u]) continue;
		vis[u] = 1;
		//cout<<u<<endl;
		for(int i=0;i<v[u].size();i++) {
			int k = v[u][i].first;
			int w = v[u][i].second;
			if(d[k] > d[u] + w) {
				d[k] = d[u] + w;
				if(!vis[k]) Q.push({d[k],k}) ; 
			}
		}
	}
	return ;
}
int main() {
	n = read();m = read();s = read();
	for(int i=1;i<=m;i++) {
		int u,k,w;
		u = read();k = read();w = read();
		v[u].push_back({k,w});
	}
	Dijkstra();
	for(int i=1;i<=n;i++) printf("%d ",d[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值