【最短路阶段性测试】题解

T1 最短路上的统计

考点:Floyd+暴力

我是真的没想到这题竟然是如此的暴力

首先,要跑一个 Floyd ,只是母庸置疑的

问题是如何求最短路上的点

现在假设 k k k 点是 i i i 点到 j j j 点的最短路上的一个点,那么,显然有 F [   i   ] [   j   ] = F [   i   ] [   k   ] + F [   k   ] [   j   ] F[\ i\ ][\ j\ ]=F[\ i\ ][\ k\ ]+F[\ k\ ][\ j\ ] F[ i ][ j ]=F[ i ][ k ]+F[ k ][ j ]

很好想:因为 k k k 点是 i i i 点到 j j j 点的最短路上的一个点,自然,我们从 i i i 点到 j j j 点会经过 k k k 点,从 i i i 点到 k k k 点的最短路和从 k k k 点到 j j j 点的最短路就必然是从 i i i 点到 j j j 点的最短路

那就也没什么好说的了

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,p,a[105][105],x,y;
int ans[105][105];
void Floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=i==j?0:0x3f3f3f3f;
		}
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		a[x][y]=a[y][x]=1;
	}
	Floyd();			//以上所有全是 Floyd 模板
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=n;k++){
				if(a[i][j]==a[i][k]+a[k][j]){			//上文已提,当 k 点是在 i 点到 j 点的最短路上
					ans[i][j]++;
				}
			}
		}
	}
	scanf("%d",&p);
	for(int i=1;i<=p;i++){
		scanf("%d%d",&x,&y);
		printf("%d\n",ans[x][y]);			//对应输出
	}
	return 0;
}

T2 ことりのおやつ

考点:Dijkstra

基本上是板题,但是在判断条件时需要增加一点

因为我们在到达该点时不能超过其极限雪量,所以,需要用到达该点的时间乘上每一秒增加的雪量再加上初始雪量与极限雪量进行比较,若大于了极限雪量,就不能到,反之,就可以到

于是,你开开心心写完了代码,交给了评测姬

在这里插入图片描述

问题出在哪了?

不考虑点心店和 ことり 家的雪

真的,审题真的很!重!要!

那么,我们就很容易得到 AC 代码了

#include<queue>
#include<cstdio>
using namespace std;
priority_queue<pair<long long int,int> > q;
const int N=10000005;
long long int head[N],Next[N],ver[N],edge[N],len;
long long int dis[N];
long long int vis[N];
long long int l[N],h[N]; 
long long int n,m,Start,End,x,y,z,qq,p;
void add(int x,int y,int z){
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra(){
	for(int i=1;i<=n;i++){
		dis[i]=0x7f7f7f7f;
	}
	dis[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis[xx]){
			continue;
		}
		vis[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis[yy]>dis[xx]+zz&&((dis[xx]+zz)*p+l[yy]<=h[yy])){
			//可以更新最短路,且不超过极限雪量或者即将到达终点
				dis[yy]=dis[xx]+zz;
				q.push(make_pair(-dis[yy],yy));
			}
		}
	}
}
int read(){
	long long int a=1,b=0;
	char ch=getchar();
	while(!(ch>='0'&&ch<='9')){
		if(ch=='-'){
			a=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		b=(b<<1)+(b<<3)+ch-'0';
		ch=getchar();
	}
	return a*b;
}
int main(){
	n=read(),m=read(),Start=read(),End=read(),qq=read(),p=read();
	for(int i=1;i<=n;i++){
		l[i]=read(),h[i]=read();
	}
	for(int i=1;i<=m;i++){
		x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	Dijkstra();
	if(dis[End]>qq||dis[End]==0x7f7f7f7f){
		printf("wtnap wa kotori no oyatsu desu!");
	}else{
		printf("%lld",dis[End]);
	}
	return 0;
}
//剩下的全是 Dijkstra 模板

没有做对的蒟蒻很想扇自己两巴掌

T3 「CQOI2005」新年好

从此以后,新年再也不好

考点:Dijkstra+搜索

我们将 5 5 5 个亲戚分别编号为 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5

对于佳佳而言,他拜访的顺序有 120 120 120 种: 1 → 2 → 3 → 4 → 5 , 1 → 2 → 3 → 5 → 4 , 1 → 2 → 4 → 3 → 5 ⋯ 1\to2\to3\to4\to5,1\to2\to3\to5\to4,1\to2\to4\to3\to5\cdots 12345,12354,12435

是的,全排列

那么,对于求最短路,我们要求六次最短路:分别是以佳佳为起点, 1 1 1 号亲戚为起点, 2 2 2 号亲戚为起点, 3 3 3 号亲戚为起点, 4 4 4 号亲戚为起点, 5 5 5 号亲戚为起点

求的最短路后,再通过一个简单的 DFS 求最小的即可

注意!注意!

由于本蒟蒻做法过于打表,请慎重食用!!!

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
priority_queue<pair<int,int> > q;
const int N=200005;
int head[N],Next[N],ver[N],edge[N],len;
int dis1[N],vis1[N];
int dis2[N],vis2[N];
int dis3[N],vis3[N];
int dis4[N],vis4[N];
int dis5[N],vis5[N];
int dis6[N],vis6[N];
int n,m,x,y,z;
int a[10];
void add(int x,int y,int z){
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra1(int Start){			//以佳佳为起点的最短路
	for(int i=1;i<=n;i++){
		dis1[i]=0x3f3f3f3f;
	}
	dis1[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis1[xx]){
			continue;
		}
		vis1[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis1[yy]>dis1[xx]+zz){
				dis1[yy]=dis1[xx]+zz;
				q.push(make_pair(-dis1[yy],yy));
			}
		}
	}
}
void Dijkstra2(int Start){			//以 1 号亲戚为起点的最短路
	for(int i=1;i<=n;i++){
		dis2[i]=0x3f3f3f3f;
	}
	dis2[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis2[xx]){
			continue;
		}
		vis2[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis2[yy]>dis2[xx]+zz){
				dis2[yy]=dis2[xx]+zz;
				q.push(make_pair(-dis2[yy],yy));
			}
		}
	}
}
void Dijkstra3(int Start){			//以 2 号亲戚为起点的最短路
	for(int i=1;i<=n;i++){
		dis3[i]=0x3f3f3f3f;
	}
	dis3[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis3[xx]){
			continue;
		}
		vis3[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis3[yy]>dis3[xx]+zz){
				dis3[yy]=dis3[xx]+zz;
				q.push(make_pair(-dis3[yy],yy));
			}
		}
	}
}
void Dijkstra4(int Start){			//以 3 号亲戚为起点的最短路
	for(int i=1;i<=n;i++){
		dis4[i]=0x3f3f3f3f;
	}
	dis4[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis4[xx]){
			continue;
		}
		vis4[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis4[yy]>dis4[xx]+zz){
				dis4[yy]=dis4[xx]+zz;
				q.push(make_pair(-dis4[yy],yy));
			}
		}
	}
}
void Dijkstra5(int Start){			//以 4 号亲戚为起点的最短路
	for(int i=1;i<=n;i++){
		dis5[i]=0x3f3f3f3f;
	}
	dis5[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis5[xx]){
			continue;
		}
		vis5[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis5[yy]>dis5[xx]+zz){
				dis5[yy]=dis5[xx]+zz;
				q.push(make_pair(-dis5[yy],yy));
			}
		}
	}
}
void Dijkstra6(int Start){			//以 5 号亲戚为起点的最短路
	for(int i=1;i<=n;i++){
		dis6[i]=0x3f3f3f3f;
	}
	dis6[Start]=0;
	q.push(make_pair(0,Start));
	while(!q.empty()){
		int xx=q.top().second;
		q.pop();
		if(vis6[xx]){
			continue;
		}
		vis6[xx]=1;
		for(int i=head[xx];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis6[yy]>dis6[xx]+zz){
				dis6[yy]=dis6[xx]+zz;
				q.push(make_pair(-dis6[yy],yy));
			}
		}
	}
}
int read(){
	int a=1,b=0;
	char ch=getchar();
	while(!(ch>='0'&&ch<='9')){
		if(ch=='-'){
			a=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		b=(b<<1)+(b<<3)+ch-'0';
		ch=getchar();
	}
	return a*b;
}			//快读
int main(){
	n=read(),m=read(),a[1]=read(),a[2]=read(),a[3]=read(),a[4]=read(),a[5]=read();
	a[0]=1;
	for(int i=1;i<=m;i++){
		x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);
	}
	int ans=2147483647;
	Dijkstra1(a[0]);
	Dijkstra2(a[1]);
	Dijkstra3(a[2]);
	Dijkstra4(a[3]);
	Dijkstra5(a[4]);
	Dijkstra6(a[5]);			//分别跑最短路
	for(int aa=1;aa<=5;aa++){
		for(int bb=1;bb<=5;bb++){
			for(int cc=1;cc<=5;cc++){
				for(int dd=1;dd<=5;dd++){
					for(int ee=1;ee<=5;ee++){
						if(aa==bb||aa==cc||aa==dd||aa==ee||bb==cc||bb==dd||bb==ee||cc==dd||cc==ee||dd==ee){
							continue;
						}			//枚举全排列
						int sum=0;
						sum+=dis1[a[aa]];			//将以佳佳家为起点到第一个亲戚家的最短路累加给答案
						if(aa==1){			//从佳佳到了 1 号亲戚家
							sum+=dis2[a[bb]];			//将以 1 号亲戚家为起点到下一个亲戚家的最短路累加给答案
						}else if(aa==2){			//从佳佳到了 2 号亲戚家
							sum+=dis3[a[bb]];			//将以 2 号亲戚家为起点到下一个亲戚家的最短路累加给答案
						}else if(aa==3){			//从佳佳到了 3 号亲戚家
							sum+=dis4[a[bb]];			//将以 3 号亲戚家为起点到下一个亲戚家的最短路累加给答案
						}else if(aa==4){			//从佳佳到了 4 号亲戚家
							sum+=dis5[a[bb]];			//将以 4 号亲戚家为起点到下一个亲戚家的最短路累加给答案
						}else if(aa==5){			//从佳佳到了 5 号亲戚家
							sum+=dis6[a[bb]];			//将以 5 号亲戚家为起点到下一个亲戚家的最短路累加给答案
						}
						if(bb==1){			//以下做法同上
							sum+=dis2[a[cc]];
						}else if(bb==2){
							sum+=dis3[a[cc]];
						}else if(bb==3){
							sum+=dis4[a[cc]];
						}else if(bb==4){
							sum+=dis5[a[cc]];
						}else if(bb==5){
							sum+=dis6[a[cc]];
						}
						if(cc==1){
							sum+=dis2[a[dd]];
						}else if(cc==2){
							sum+=dis3[a[dd]];
						}else if(cc==3){
							sum+=dis4[a[dd]];
						}else if(cc==4){
							sum+=dis5[a[dd]];
						}else if(cc==5){
							sum+=dis6[a[dd]];
						}
						if(dd==1){
							sum+=dis2[a[ee]];
						}else if(dd==2){
							sum+=dis3[a[ee]];
						}else if(dd==3){
							sum+=dis4[a[ee]];
						}else if(dd==4){
							sum+=dis5[a[ee]];
						}else if(dd==5){
							sum+=dis6[a[ee]];
						}
						ans=min(ans,sum);
					}
				}
			}
		}
	}
	printf("%d",ans);			//输出答案
	return 0;
}

我敢打赌,当年省选肯定有人做法和我一样!绝对有!

T4 算法导论

Tips:由于标程有误,85 分代码即为 100 分

考点:次短路

最短路大家都会,那次短路呢

还记得在很久很久以前,我们是如何求次小数的呢?

我们可以定义最小数 m i n n minn minn 和次小数 m i n n n minnn minnn ,在遍历每一个元素时,如果它比最小数还要小,就把最小数的值赋给次短路,再把该元素的值赋给最小数;如果它在最小数与次小数之间,则只更新次小数;否则什么也不做

我们可以用类似的思想解决次短路

#include<queue>
#include<cstdio>
using namespace std;
const int N=1000005;
int head[N],Next[N],ver[N],len;
long long int edge[N];
long long int dis[N][2];			//dis[i][0] 表示以 i 为终点的最短路,dis[i][1] 表示以 i 为终点的次短路
int n,m,Start,End,x,y;
long long int z,zz;
struct node{
	int kkksc03,num,sum;
}nd;
queue<node> q;
void add(int x,int y,long long int z){
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
void Dijkstra(){
	for(int i=1;i<=n;i++){
		dis[i][0]=dis[i][1]=0x3f3f3f3f;
	}
	dis[Start][0]=0;
	nd.kkksc03=0,nd.num=Start,nd.sum=0;
	q.push(nd);
	while(!q.empty()){
		nd.kkksc03=q.front().kkksc03,nd.num=q.front().num,nd.sum=q.front().sum;
		int KKKSC03=nd.kkksc03,NUM=nd.num;
		long long int SUM=nd.sum;
		q.pop();
		if(dis[NUM][KKKSC03]!=SUM){			//如果不相等,说明已经被更新过了,可代替 vis 数组
			continue;
		}
		for(int i=head[NUM];i;i=Next[i]){
			int yy=ver[i],zz=edge[i];
			if(dis[yy][1]<=dis[NUM][KKKSC03]+zz){			//如果当前路径权值比次短路大
				continue;			//不理
			}
			if(dis[yy][0]<=dis[NUM][KKKSC03]+zz){			//如果当前路径权值在最短路与次短路之间
				dis[yy][1]=dis[NUM][KKKSC03]+zz;			//只更新次短路
				nd.kkksc03=1,nd.num=yy,nd.sum=dis[yy][1];			//相应压栈操作
				q.push(nd);
			}else{			//如果当前路径权值比最短路小
				dis[yy][1]=dis[yy][0];			//把最短路的值赋给次短路
				nd.kkksc03=1,nd.num=yy,nd.sum=dis[yy][1];			//相应压栈操作
				q.push(nd);
				dis[yy][0]=dis[NUM][KKKSC03]+zz;			//把当前路径权值赋给最短路
				nd.kkksc03=0,nd.num=yy,nd.sum=dis[yy][0];			//相应压栈操作
				q.push(nd);
			}
		}
	}
}
int main(){
	scanf("%d%d%d%d",&n,&m,&Start,&End);
	for(int i=1;i<=m;i++){
		scanf("%d%d%lld%lld",&x,&y,&z,&zz);
		add(x,y,z*360360/zz);			//为避免 double 精度问题,需乘上 [1,15] 的最小公倍数
		add(y,x,z*360360/zz);
	}
	Dijkstra();
	printf("%lld",dis[End][1]/360360);			//把多乘的 360360 除回来
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值