<—点个赞吧QwQ
宣传一下算法提高课整理{:target=”_blank”}
给定一张 $N$ 个点(编号 $1,2…N$),$M$ 条边的有向图,求从起点 $S$ 到终点 $T$ 的第 $K$ 短路的长度,路径允许重复经过点或边。
注意: 每条最短路中至少要包含一条边。
输入格式
第一行包含两个整数 $N$ 和 $M$。
接下来 $M$ 行,每行包含三个整数 $A,B$ 和 $L$,表示点 $A$ 与点 $B$ 之间存在有向边,且边长为 $L$。
最后一行包含三个整数 $S,T$ 和 $K$,分别表示起点 $S$,终点 $T$ 和第 $K$ 短路。
输出格式
输出占一行,包含一个整数,表示第 $K$ 短路的长度,如果第 $K$ 短路不存在,则输出 $\-1$。
数据范围
$1 \\le S,T \\le N \\le 1000$,
$0 \\le M \\le 10^4$,
$1 \\le K \\le 1000$,
$1 \\le L \\le 100$
输入样例:
2 2
1 2 5
2 1 4
1 2 2
输出样例:
14
思路
- 第$ K $短路,就是终点出队$ K $次的距离。注意:当起点和终点一样时,$K ++$
- 只要从$ S $能到达$ T$,就一定存在第K短路,故不存在则一定不能从$ S $到达$ T$
- 估价函数:从该点到终点的最短距离,即求一遍$ \text{Dijkstra}$,大于等于$ 0$,小于等于真实值
- 取出堆顶,记录出队次数,把该点能枚举到的所有点都放入小根堆
代码
#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair <int,int> PII;
typedef pair <int,PII> PIII;
const int N = 1010,M = 200010;
int n,m,S,T,K;
int h[N],rh[N],e[M],w[M],ne[M],idx;
int dist[N],cnt[N];
bool st[N];
void add (int h[],int a,int b,int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
void dijkstra () {
priority_queue <PII,vector <PII>,greater <PII>> heap;
heap.push ({0,T});
memset (dist,0x3f,sizeof (dist));
dist[T] = 0;
while (heap.size ()) {
auto t = heap.top ();
heap.pop ();
int ver = t.y;
if (st[ver]) continue;
st[ver] = true;
for (int i = rh[ver];~i;i = ne[i]) {
int j = e[i];
if (dist[j] > dist[ver] + w[i]) {
dist[j] = dist[ver] + w[i];
heap.push ({dist[j],j});
}
}
}
}
int Astar () {
priority_queue <PIII,vector <PIII>,greater <PIII>> heap;
heap.push ({dist[S],{0,S}});
while (heap.size ()) {
auto t = heap.top ();
heap.pop ();
int ver = t.y.y,distance = t.y.x;
cnt[ver]++;
if (cnt[T] == K) return distance;
for (int i = h[ver];~i;i = ne[i]) {
int j = e[i];
if (cnt[j] < K) heap.push ({distance+w[i]+dist[j],{distance+w[i],j}});
}
}
return -1;
}
int main () {
memset (h,-1,sizeof (h));
memset (rh,-1,sizeof (rh));
cin >> n >> m;
for (int i = 0;i < m;i++) {
int a,b,c;
cin >> a >> b >> c;
add (h,a,b,c);
add (rh,b,a,c);
}
cin >> S >> T >> K;
if (S == T) K++;
dijkstra ();
cout << Astar () << endl;
return 0;
}
你们怎么都不解释
if(cnt[j]<k)
qwq因为很好懂啊,就是出对次数<k时更新
为什么出对次数>k时就不用更新了?这里没懂
出队第k次就代表找到答案了
但是yxc视频里面说 不是终点 可以大于k次
那按y总说的算
hh
的确,这个讲理论确实海涩
其实是我太菜了不是你太菜了,是那块本来讲的就不太明白,我说下我的理解:
此处代码有两种写法:
网上大神写法:
我的写法:
原因:见$AcWing$给出错误时的数据用例:
#### 感悟
① 自我认为我的办法才是正解,因为你想把估值函数入队列,还指望着函数值小的优先,那如果
d + w[i] + dist[v]>=INF
,再往里放就是无效操作,而dist[v]
是有可能等于INF
的,因为$$\large S->v-\ngtr T$$
此时,$v$就是 一个无效转移点,不用入队列,一次都不用!
② 通过错误的 数据用例 来反思、推导,加深理解非常重要,此处给$AcWing$满分,比某谷要强的多!与学会自我创造测试用例一样有用,在以后的学习中一次要重视起来。
不错,tql %%%
orz
hh tql
k大于1 的时候,但是t没有出边,那么也没有答案