原理的话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紧急救援
这个题就是个简单的模板题,没啥特别难想的点。。。
不过,,,
其实这个题对于语文一直不太好的我来说容易理解错误
有一个点要注意的是,所谓的“输出最短路的数量”,是指所有最短路径,而非单条最短路径的路径数量
这个东西我想了两天一夜超过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;
}