dijkstra专题

原理的话csdn上很多人说的都非常清楚了,这里就给一些题和题解,同时供自己以点带面复习。

模板略,持续性慢慢更新hh。

1. dijkstra序列

PAT甲级真题1163

对于一个给定的图,可能有多个 Dijkstra 序列。

例如,{5,1,3,4,2}{5,1,3,4,2} 和 {5,3,1,2,4}{5,3,1,2,4} 都是给定图的 Dijkstra 序列。

注意,序列中的第一个顶点即为指定的特定源顶点。

你的任务是检查给定的序列是否是 Dijkstra 序列。

输入格式

第一行包含两个整数 N 和 M,表示图中点和边的数量。

点的编号 1∼N。

接下来 MM 行,每行包含三个整数 a,b,c,表示点 a 和点 b 之间存在一条无向边,长度为 c。

再一行包含整数 K,表示需要判断的序列个数。

接下来 K 行,每行包含一个 1∼N 的排列,表示一个给定序列。

输出格式

共 K行,第 i 行输出第 K个序列的判断,如果序列是 Dijkstra 序列则输出 Yes,否则输出 No

数据范围

1≤N≤1000,
1≤M≤105,
1≤a,b≤N,
1≤c≤100,
1≤K≤100,
保证给定无向图是连通图

样例

5 7
1 2 2
1 5 1
2 3 1
2 4 1
2 5 2
3 5 1
3 4 1
4
5 1 3 4 2
5 3 1 2 4
2 3 4 5 1
3 2 1 5 4
Yes
Yes
Yes
No

 dijkstra于贪心的思想,每次选择最短的路径,那么意味着如果所给定的序列如果是最短路最短路径,那么进行dijkstra的时候就会顺着走;如果不是,就会更新序列,此时返回false即可。

如果用一个数res记录走过的路径权值之和用来比较dist[y],或者手动推图后对样例所给序列提出质疑因为根本走不到的时候。

你手动模拟就发现诶不对啊比如5-1-3-2-4这个序列,1-3是不能直接联通的,g[1][3]就是0x3f3f3f3f了,怎么可能等,也走不通,明明是5-1-2-3-4才对

而5-4最短权值也仅仅是2,枉论所有边加起来。

这里就体现对dijkstra算法的思想理解还不够到位。

每次dijkstra更新的是什么?是到起点的最短距离,不是到当前这个点。

2-3-4-5-1中更新的顺序是2-3后是2-4,而非3-4;接着是2-5,2-1

是每次距离起点的最短距离,而非距离当前这个点。

#include<bits/stdc++.h>
using namespace std;

const int N=1010; 
int g[N][N];
int n,m;
int h[N],dist[N]; //从某个点到终点的最短距离 
bool st[N];

int dijkstra(){
	memset(st,0,sizeof(st));
	memset(dist,0x3f,sizeof(dist));
	dist[h[0]]=0;
	for(int i=0;i<n;i++){
		int t=h[i];
		//枚举到每个点的距离
		for(int j=1;j<=n;j++){
			if(!st[j]&&dist[t]>dist[j]){
				t=j;
				return false;
			}
		}
		st[t]=true;
		//更新距离
		for(int j=1;j<=n;j++){
			if(dist[j]>dist[t]+g[t][j]){
				dist[j]=dist[t]+g[t][j];
			}
		}
	}
	return true;
}

int main(){
	cin>>n>>m;
	memset(g,0x3f,sizeof(g));
	for(int i=0;i<m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b]=g[b][a]=min(g[a][b],c);
	}
	int k;
	cin>>k;
	while(k--){
		for(int i=0;i<n;i++){
			cin>>h[i];
		}
		int t=dijkstra();
		if(t) puts("Yes");
		else puts("No");
	}
	return 0;
}

L2-001紧急救援

PTA | 程序设计类实验辅助教学平台

这个题就是个简单的模板题,没啥特别难想的点。。。

不过,,,

其实这个题对于语文一直不太好的我来说容易理解错误
有一个点要注意的是,所谓的“输出最短路的数量”,是指所有最短路径,而非单条最短路径的路径数量
这个东西我想了两天一夜超过20h都在思索反复推敲去查资料手动模拟PTA扒数据百思不得其解哈哈哈哈哈我宣布个事!我是sb!淦!

需要注意的是这个题里面,最短路径如果你想要用队列存储的话,说明你对dijkstra的理解还不到位
dijkstra是枚举距离0最短的点,假设0-4为1,4-5为1,0-7为1,那么先枚举0-4,第二步不是4-5而是0-7
所以队列是不可行的,故采取数组下标next的形式,存储其上一个结点,既0-7-3-2-1这个序列,1存储的是2,2存3,3存7.
这样我们倒回去压入栈然后输出即可。

同时注意,路径计算的时候要加上前面的路径数量,而非直接+1。因为你前面的点可以多种方法到达,而简单+1默认只有一条路径达到,漏掉了情况。

其他的没啥问题了,就是个简单的模板题,注意判断条件即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,s,d;
const int N=510;
int g[N][N];
int dist[N];//道路长度,既权重
int fire[N];//距离起点所召集的消防队数量
int much[N];//单个城市救援队数量 
bool st[N];//是否访问过 
int path[N];
int way[N];

bool check(int t,int j){
    if(dist[t]>dist[j])
        return true;
    else if(dist[t]==dist[j]&&fire[t]<=fire[j])
        return true;
    return false;
}

int dijkstra(){
    memset(dist,0x3f,sizeof(dist));
    dist[s]=0;
    way[s]=1;
    memset(fire,0,sizeof fire);
    fire[s]=much[s]; 
    for(int i=0;i<n;i++){
        int t=-1;
        for(int j=0;j<n;j++)
            if(!st[j]&&(t==-1||check(t,j)))
                t=j;
        st[t]=true;
        //更新
        for(int j=0;j<n;j++){
            if(!st[j]&&dist[j]>dist[t]+g[t][j]){
                dist[j]=dist[t]+g[t][j];
                path[j]=t;
                fire[j]=fire[t]+much[j];
                way[j]=way[t];
            }else if(!st[j]&&dist[j]==dist[t]+g[t][j]){
                way[j]+=way[t];
                //如果只是+1,你前面的那个点如果有多种方式到达,相当于漏掉了几种情况 
                if(fire[j]<fire[t]+much[j]){
                    fire[j]=fire[t]+much[j];
                    path[j]=t;
                }
            }
        }    
    }
    return fire[d];
}

int main(){
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m>>s>>d;
    memset(g,0x3f,sizeof g);
    for(int i=0;i<n;i++){
        cin>>much[i];
    }
    for(int i=0;i<n;i++)
        g[i][i]=0;
    for(int i=0;i<m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);
    }
    int t=dijkstra();    
    stack<int>stk;
    cout<<way[d]<<' '<<t<<endl;
    //输出路径
    
    for(int i=d;i!=s;i=path[i]){
        stk.push(i);
    }
    cout<<s; //出发点未压入栈 
    while(stk.size()){
        cout<<' '<<stk.top();
        stk.pop();
    }
    
    cout<<endl; 
    return 0;
}

可以考虑堆优化,但是比较懒,既然能过就没多写hh

附带一下扒出来的第二个数据点:

8 10 0 1
2 1 15 1 1 1 1 3
0 4 1
0 3 2
0 7 1
4 5 1
3 2 1
7 3 1
3 6 1
2 1 1
1 6 1
2 5 1

自己写的第二个数据点没过的可以看看哪里出了问题。

PAT甲级真题1030

这个就不妨题目了,因为和上一道题非常相似,不能说非常相似,只能说一模一样,要是写题解那也差不多了hh。

但是为什么还要整理呢?诶,那是因为这道题的图挺稀疏的

稀疏图用朴素dijkstra容易爆掉,所以用堆优化的dijkstra

既然之前没用,那就现在用吧hh

#include<bits/stdc++.h>
using namespace std;

//既然是稀疏图,不如就用堆优化了

const int N=2000;
int e[N],ne[N],h[N],idx=1,w[N];
typedef pair<int,int> PII;
priority_queue<PII,vector<PII>,greater<PII> >heap;
int n,m,s,d;
int dist[N],path[N];
bool st[N];
int value[N],sum[N];

void add(int a,int b,int c,int val){
	e[idx]=b,ne[idx]=h[a],w[idx]=c,value[idx]=val,h[a]=idx++;
}

int dijkstra(){
	memset(dist,0x3f,sizeof dist);
	dist[s]=0;
	w[s]=0;
	heap.push({0,s});
	while(heap.size()){
		PII t=heap.top();
		heap.pop();
		int ver=t.second,distance=t.first;
		if(st[ver]) continue;
		st[ver]=true;
		//更新 
		for(int i=h[ver];i!=-1;i=ne[i]){
			int j=e[i];
			if(dist[j]>distance+w[i]){
				dist[j]=distance+w[i];
				heap.push({dist[j],j});
				path[j]=ver;
				sum[j]=sum[ver]+value[i];
			}else if(dist[j]==distance+w[i]&&sum[j]>sum[ver]+value[i]){
				//dist[j]=distance+w[i];
				heap.push({dist[j],j});
				path[j]=ver;
				sum[j]=sum[ver]+value[i];
			}
		}
	} 
	return dist[d];
}

int main(){
	memset(h,-1,sizeof h);
	cin>>n>>m>>s>>d;
	for(int i=1;i<=m;i++){
		int a,b,c,val;
		cin>>a>>b>>c>>val;
		add(a,b,c,val),add(b,a,c,val);
	}
	int t=dijkstra();
	stack<int>stk;
	int x=d;
	while(x!=s){
		stk.push(x);
		x=path[x];
	}
	stk.push(s);
	while(stk.size()){
		cout<<stk.top()<<' ';
		stk.pop();
	}
	cout<<t<<' '<<sum[d];
	return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值