GPLT-天梯赛 最短路问题(1)

1、L3-1 直捣黄龙 (30 分)
在这里插入图片描述
思路:这题就是一个dijkstra,但是不同的是他有多个更新路径的变量,我们要一个个进行判断即可,但是要注意的是,对于矩阵的初始化,我们一定要将它全部初始化为正无穷,不能将自己到自己的初始化为0,这样是记录不了路径的。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int g[maxn][maxn];
int dis[maxn],path[maxn],city[maxn],val[maxn];
/*dis数组是距离 
  path数组是路径
  city数组是攻下城市数
  val数组是杀敌数
  cnt数组是路径条数 
*/  
int vis[maxn];
int cnt[maxn];
int a[maxn];
map<string,int> mp;
map<int,string> mpx;
int n,k;
string st,ed;
void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[1]=0,path[1]=-1,city[1]=0,val[1]=0;
	cnt[1]=1;
	for(int i=1; i<n; i++){
		int k=-1;
		for(int j=1; j<=n; j++){
			if(!vis[j]&&(k==-1||dis[k]>dis[j])){
				k=j;
			}
		}
		vis[k]=1;
		for(int j=1; j<=n; j++){
			if(dis[j]>dis[k]+g[k][j]){
//				cout << "a " << j << " " << k << endl;
				dis[j]=dis[k]+g[k][j];
				path[j]=k;
				city[j]=city[k]+1;
				val[j]=val[k]+a[j];
				cnt[j]=cnt[k];
			}
			else if(dis[j]==dis[k]+g[k][j]){
				cnt[j]=cnt[k]+cnt[j]; 
				if(city[j]<city[k]+1){
//					cout << "b " << j << " " << k << endl;
					city[j]=city[k]+1;
					path[j]=k;
					val[j]=val[k]+a[j];
					
				}
				else if(city[j]==city[k]+1){
					if(val[j]<val[k]+a[j]){
//						cout << "c " << j << " " << k << endl;
						val[j]=val[k]+a[j];
						path[j]=k;
					}
				}
			}
		}
	}
}
int main(){
	cin >> n >> k >> st >> ed;
    //注意一下初始化,一定要全部初始化为正无穷
// 	for(int i=1; i<=n; i++){
// 		for(int j=1; j<=n; j++){
// 			if(i==j) g[i][j]=0;
// 			else g[i][j]=0x3f3f3f3f;
// 		}
// 	}
	memset(g,0x3f,sizeof g);
	mp[st]=1;
	mpx[1]=st;
	for(int i=2; i<=n; i++){
		string s;
		int x;
		cin >> s >> x;
		mp[s]=i;
		mpx[i]=s;
		a[i]=x;
	}
	for(int i=1; i<=k; i++){
		string s,sx;
		int x;
		cin >> s >> sx >> x;
		g[mp[s]][mp[sx]]=g[mp[sx]][mp[s]]=min(g[mp[s]][mp[sx]],x);
	}
	dijkstra();
	int x=mp[ed];
	vector<int> v;
	while(x!=-1){
//		cout << x << endl;
		v.push_back(x);
		x=path[x];
	}
	for(int i=v.size()-1; i>=0; i--){
		if(i!=v.size()-1) printf("->");
		cout << mpx[v[i]];
	}
	puts("");
	printf("%d %d %d",cnt[mp[ed]],dis[mp[ed]],val[mp[ed]]);
	return 0;
}

堆优化解法:

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1e5+10;
int h[maxn],e[maxn],w[maxn],ne[maxn],idx;
int dis[maxn],vis[maxn],city[maxn],cnt[maxn],val[maxn],path[maxn];
int n,k;
int a[maxn];
string st,ed;
map<string,int> mp;
map<int,string> mpx;
void add(int a, int b, int c){
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	priority_queue<pii,vector<pii>,greater<pii> > q;
	dis[1]=0,cnt[1]=1,city[1]=0,val[1]=0,path[1]=-1;
	q.push({0,1});
	while(!q.empty()){
		auto t=q.top();
		q.pop();
		int ver=t.second;
		if(vis[ver]) continue;
		vis[ver]=1;
		for(int i=h[ver]; i!=-1; i=ne[i]){
			int j=e[i];
			if(dis[j]>dis[ver]+w[i]){
				dis[j]=dis[ver]+w[i];
				cnt[j]=cnt[ver];
				city[j]=city[ver]+1;
				val[j]=val[ver]+a[j];
				path[j]=ver;
				q.push({dis[j],j});
				//只需要这里入队即可 
			}
			else if(dis[j]==dis[ver]+w[i]){
				cnt[j]=cnt[j]+cnt[ver];
				if(city[j]<city[ver]+1){
					city[j]=city[ver]+1;
					val[j]=val[ver]+a[j];
					path[j]=ver;
				}
				else if(city[j]==city[ver]+1){
					if(val[j]<val[ver]+a[j]){
						val[j]=val[ver]+a[j];
						path[j]=ver;
					}
				}
			}
		}
	}
	int x=mp[ed];
	stack<int> st;
	while(x!=-1){
		st.push(x);
		x=path[x];
	}
	int flag=0;
	while(!st.empty()){
		if(flag) printf("->");
		cout << mpx[st.top()];
		flag=1;
		st.pop();
	}
	puts("");
	printf("%d %d %d",cnt[mp[ed]],dis[mp[ed]],val[mp[ed]]);
}
int main(){
	memset(h,-1,sizeof h);
	cin >> n >> k >> st >> ed;
	mp[st]=1;
	mpx[1]=st;
	for(int i=2; i<=n; i++){
		string s;
		int x;
		cin >> s >> x;
		mp[s]=i;
		mpx[i]=s;
		a[i]=x;
	}
	for(int i=1; i<=k; i++){
		string s,sx;
		int x;
		cin >> s >> sx >> x;
		add(mp[s],mp[sx],x),add(mp[sx],mp[s],x);
	}
	dijkstra();
	return 0;
}

2、L3-005 垃圾箱分布 (30 分)
在这里插入图片描述
思路:这题主要是题意理解上比较难一点。垃圾箱到居民点的最短距离里的最大值不能超过d,这就是题意中的不太远;然后垃圾箱到居民点的最短距离里的最小值要最大的,这个就是不太近,如果前面两个情况都符合的,就找到居民点距离和最小的,如果还相同,那就找编号最小的垃圾箱。注意一点的是,我们最好用邻接表做,矩阵做也可以,但不要去更新无谓的点,否则测试点2和3会错。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
typedef pair<int,int> pii;
map<string,int> mp;
int h[maxn],w[maxn],e[maxn],ne[maxn],idx;
int dis[20][maxn];
int vis[maxn];
int sumx[maxn];
int minn[maxn], maxx[maxn];
int flag[maxn];
void add(int a, int b, int c){
	w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int n,m,k,d;
void dijkstra(int u){
	memset(vis,0,sizeof vis);
	priority_queue<pii,vector<pii>,greater<pii> > q;
	int uu=u-1000;
//	cout << uu << endl;
//	cout << u << endl;
	for(int i=1; i<=1020; i++){
		dis[uu][i]=0x3f3f3f3f;
	}
	dis[uu][u]=0;
	q.push({0,u});
	while(!q.empty()){
		auto t=q.top();
		q.pop();
		int ver=t.second;
		if(vis[ver]) continue;
		vis[ver]=1;
		for(int i=h[ver]; i!=-1; i=ne[i]){
			int j=e[i];
			if(dis[uu][j]>dis[uu][ver]+w[i]){
				dis[uu][j]=dis[uu][ver]+w[i];
				q.push({dis[uu][j],j});
			}
		}
	}
	for(int i=1; i<=n; i++){
		minn[u]=min(minn[u],dis[uu][i]);
		maxx[u]=max(maxx[u],dis[uu][i]);
		sumx[u]+=dis[uu][i];
		if(dis[uu][i]==0x3f3f3f3f) flag[u]=1;
	}
}
int main(){
	memset(h,-1,sizeof h);
	memset(minn,0x3f3f3f3f,sizeof minn);
	cin >> n >> m >> k >> d;
	mp["G1"]=1001,mp["G2"]=1002,mp["G3"]=1003,mp["G4"]=1004,mp["G5"]=1005;
	mp["G6"]=1006,mp["G7"]=1007,mp["G8"]=1008,mp["G9"]=1009,mp["G10"]=1010;
	for(int i=0; i<k; i++){
		int x,y,z;
		string yy;
		string xx;
		cin >> xx >> yy >> z;
		if(xx[0]=='G') x=mp[xx];
		else x=stoi(xx);
		if(yy[0]=='G') y=mp[yy];
		else y=stoi(yy);
		add(x,y,z),add(y,x,z);
	}
	for(int i=1001; i<=1000+m; i++){
		dijkstra(i);
	}
	double res=0;
	int k=-1;
	int sumxx=0;
	for(int i=1001; i<=1000+m; i++){
		if(!flag[i]){
			//不太远的意思是最短路中最大值不小于d,不太近的意思是最短路中最小值最大 
			if(maxx[i]<=d&&minn[i]>res){
				res=minn[i];
				k=i;
				sumxx=sumx[i];
			}
			//注意要判断和更小的 
			else if(maxx[i]<=d&&minn[i]==res){
				if(sumx[i]<sumxx){
					sumxx=sumx[i];
					res=minn[i];
					k=i;
				}
			}
		}
	}	
	if(k==-1){
		puts("No Solution");
		return 0;
	}
	printf("G%d\n",k-1000);
	double resx=minn[k];
	printf("%.1f %.1f",resx,sumx[k]*1.0/n+0.005);
	return 0;
}

3、L3-007 天梯地图 (30 分)
在这里插入图片描述
思路:数据处理上难了一点,其他还好

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn = 1e6+10;//注意一下这里,开大一点 
int h[maxn],e[maxn],ne[maxn],wt[maxn],wd[maxn],idx;
int dist[maxn],disd[maxn],disp[maxn];
int patht[maxn],pathd[maxn];
int vis[maxn];
int n,m;
int st,ed;
void add(int a, int b, int c, int d){
	wt[idx]=d,wd[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dijkstra1(){
	priority_queue<pii,vector<pii>,greater<pii> > q;
	memset(vis,0,sizeof vis);
	memset(dist,0x3f,sizeof dist);
	memset(disd,0x3f,sizeof disd);
	dist[st]=0,disd[st]=0,patht[st]=-1;
	q.push({0,st});
	while(!q.empty()){
		auto t=q.top();
		q.pop();
		int ver=t.second;
		if(vis[ver]) continue;
		vis[ver]=1;
		for(int i=h[ver]; i!=-1; i=ne[i]){
			int j=e[i];
			if(dist[j]>dist[ver]+wt[i]){
				dist[j]=dist[ver]+wt[i];
				patht[j]=ver;
				disd[j]=disd[ver]+wd[i];
				q.push({dist[j],j});
			}
			else if(dist[j]==dist[ver]+wt[i]){
				if(disd[j]>disd[ver]+wd[i]){
					disd[j]=disd[ver]+wd[i];
					patht[j]=ver;
				}
			}
		}
	}
}
void dijkstra2(){
	priority_queue<pii,vector<pii>,greater<pii> > q;
	memset(vis,0,sizeof vis);
	memset(disd,0x3f,sizeof disd);
	memset(disp,0x3f,sizeof disp);
	disd[st]=0,disp[st]=1,pathd[st]=-1;
	q.push({0,st});
	while(!q.empty()){
		auto t=q.top();
		q.pop();
		int ver=t.second;
		if(vis[ver]) continue;
		vis[ver]=1;
		for(int i=h[ver]; i!=-1; i=ne[i]){
			int j=e[i];
			if(disd[j]>disd[ver]+wd[i]){
				disd[j]=disd[ver]+wd[i];
				pathd[j]=ver;
				disp[j]=disp[ver]+1;
				q.push({disd[j],j});
			}
			else if(disd[j]==disd[ver]+wd[i]){
				if(disp[j]>disp[ver]+1){
					disd[j]=disd[ver]+1;
					pathd[j]=ver;
				}
			}
		}
	}
}
vector<int> v1,v2;
int main(){
	cin >> n >> m;
	memset(h,-1,sizeof h);
	while(m--){
		int a,b,c,d,e;
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
		if(c==1) add(a,b,d,e);
		else{
			add(a,b,d,e);
			add(b,a,d,e);
		}
	}
	scanf("%d%d",&st,&ed);
	dijkstra1();//求最短时间的路线
	dijkstra2();//求最短距离的路线 
	int flag=0;
	v1.push_back(ed),v2.push_back(ed);
	int cnt1=ed, cnt2=ed;
	while(cnt1!=st){
		v1.push_back(patht[cnt1]);
		cnt1=patht[cnt1];
	}
	while(cnt2!=st){
		v2.push_back(pathd[cnt2]);
		cnt2=pathd[cnt2];
	}
	if(v1.size()==v2.size()){
		for(int i=0; i<v1.size(); i++){
			if(v1[i]!=v2[i]){
				flag=1;
				break;
			}
		}
	}
	else{
		flag=1;
	}
	if(!flag){
		printf("Time = %d; Distance = %d: ",dist[ed],disd[ed]);
		for(int i=v1.size()-1; i>=0; i--){
			if(i!=v1.size()-1) printf(" => ");
			printf("%d",v1[i]);
		}
	}
	else{
		printf("Time = %d: ",dist[ed]);
		for(int i=v1.size()-1; i>=0; i--){
			if(i!=v1.size()-1) printf(" => ");
			printf("%d",v1[i]);
		}
		puts("");
		printf("Distance = %d: ",disd[ed]);
		for(int i=v2.size()-1; i>=0; i--){
			if(i!=v2.size()-1) printf(" => ");
			printf("%d",v2[i]);
		}
	}
	return 0;
}

4、7-11 城市间紧急救援 (25 分)
在这里插入图片描述
思路:这题是最短路,经典的多个更新最短路问题(我是这么叫的),没什么好说的,直接看代码吧。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int n,m,s,d;
int g[maxn][maxn];//图 
int a[maxn];//救援队数目 
int dis[maxn];//距离 
int cnt[maxn];//路径数目 
int vis[maxn];//标记数组 
int path[maxn];//路径记录数组 
int val[maxn];//救援队数组 
void dijkstra(){
	memset(dis,0x3f,sizeof dis);
	dis[s]=0,cnt[s]=1,val[s]=a[s],path[s]=-1;
	for(int i=1; i<n; i++){
		int k=-1;
		for(int j=0; j<n; j++){
			if(!vis[j]&&(k==-1||dis[k]>dis[j])) k=j;
		}
		vis[k]=1;
		for(int j=0; j<n; j++){
			if(dis[j]>dis[k]+g[k][j]){
				dis[j]=dis[k]+g[k][j];
				val[j]=val[k]+a[j];
				cnt[j]=cnt[k];
				path[j]=k;
			}
			else if(dis[j]==dis[k]+g[k][j]){
				cnt[j]=cnt[k]+cnt[j];
				if(val[j]<val[k]+a[j]){
					val[j]=val[k]+a[j];
					path[j]=k;
				}
			}
		}
	}
	printf("%d %d\n",cnt[d],val[d]);
	int x=d;
	stack<int> st;
	while(x!=-1){
		st.push(x);
		x=path[x];
	}
	int flag=0;
	while(!st.empty()){
		if(flag) printf(" ");
		printf("%d",st.top());
		st.pop();
		flag=1;
	}
}
int main(){
	cin >> n >> m >> s >> d;
	memset(g,0x3f,sizeof g);
	for(int i=0; i<n; i++) cin >> a[i];
	for(int i=0; i<m; i++){
		int x,y,z;
		cin >> x >> y >> z;
		g[x][y]=g[y][x]=min(g[x][y],z);
	}
	dijkstra();
	return 0;
}

5、L3-022 地铁一日游 (30 分)
在这里插入图片描述
思路:这题要求森森能够拍照的地铁站点,其实就是求所有终点站和分别在不同花费下所能走到最远的站点的递增序列。难点在于我们搞定最短路后怎么才能得到他在不同花费下的所能到达的最远的点。我们可以用一个map处理,mp[2+g(i,j)/k]不断进行取max的操作就可以得到。然后要注意的是,我们这么做得到的其实就是将以每个点为起点在每个花费下所能到达的最远点的集合,然后我们不能遗漏,所以在输出答案的时候就要直接通过到达的点再遍历他能到达的点。这题我一开始也没怎么搞懂,后来也是读了大佬的代码才懂的。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
typedef long long ll;
int n,m,k,q;
ll dis[maxn][maxn];
set<int> s[maxn];
int vis[maxn];
int visx[maxn];
void dfs(int u){
	for(auto i:s[u]){
		if(!visx[i]){
			visx[i]=1;
			dfs(i);
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	getchar();
	for(int i=1; i<=n; i++){
		for(int j=1; j<=n; j++){
			if(i==j) dis[i][j]=0;
			else dis[i][j]=0x3f3f3f3f;
		}
	}
	for(int i=1; i<=m; i++){
		string s;
		getline(cin,s);
		vector<ll> v;
		int j=0;
		for(int i=0; i<s.size(); i++){
			if(s[i]==' '){
				v.push_back((ll)(stoi(s.substr(j,i-j))));
//				cout << stoi(s.substr(j,i-j)) << endl;
				j=i+1;
			}
			else if(i==s.size()-1){
				v.push_back((ll)(stoi(s.substr(j,s.size()))));
//				cout << stoi(s.substr(j,s.size())) << endl;
			}
		}
//		for(int i=0; i<v.size(); i++) cout << v[i] << " ";
//		puts("");
		for(int i=0; i<v.size()-2; i+=2){
//			cout << v[i] << " " << v[i+1] << " " << v[i+2] << endl;
			dis[v[i]][v[i+2]]=dis[v[i+2]][v[i]]=min(v[i+1],dis[v[i]][v[i+2]]);
		}
		vis[v[0]]=1,vis[v[v.size()-1]]=1;
	}
//	for(int i=1; i<=n; i++) cout << vis[i] <<" ";
//	cout << endl;
	
	for(int k=1; k<=n; k++){
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
	for(int i=1; i<=n; i++){
		map<ll,ll> mp;
		for(int j=1; j<=n; j++){
			if(dis[i][j]==0x3f3f3f3f) continue;
			mp[2+dis[i][j]/k]=max(mp[2+dis[i][j]/k],dis[i][j]);
		}
		for(int j=1; j<=n; j++){
			if(dis[i][j]==mp[2+dis[i][j]/k]||(i!=j&&vis[j]&&dis[i][j]!=0x3f3f3f3f)){
				s[i].insert(j);
			}
		}
	}
	scanf("%d",&q);
	while(q--){
		int x;
		scanf("%d",&x);
		s[x].insert(x);
		memset(visx,0,sizeof visx);
		visx[x]=1;
		dfs(x);
		for(int i=1; i<=n; i++){
			if(visx[i]) s[x].insert(i);
		}
		int flag=0;
		for(auto i:s[x]){
			if(flag) printf(" ");
			printf("%d",i);
			flag=1;
		}
		puts("");
	}
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值