dijkstra 为什么不能解决解决负权边最短路问题
加入每条边去松弛每个点到起点的距离
dist[b] = min(dist[b], backup[a] + w);
为什么需要back[a]数组
为了避免如下的串联情况, 在边数限制为一条的情况下,节点3的距离应该是3,但是由于串联情况,利用本轮更新的节点2
更新了节点3的距离,所以现在节点3的距离是2。
正确做法是用上轮节点2更新的距离--无穷大
,来更新节点3, 再取最小值,所以节点3离起点的距离是3。
for (int i = 0; i < k; i ++ )
{
memcpy(backup, dist, sizeof dist);
for (int j = 0; j < m ; j ++ )
{
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
dist[b] = min(dist[b], backup[a] + w);
}
}
为什么是dist[n]>0x3f3f3f3f/2
, 而不是dist[n]>0x3f3f3f3f
5号节点距离起点的距离是无穷大,利用5号节点更新n号节点距离起点的距离,将得到$10^9-2$, 虽然小于$10^9$, 但并不存在最短路,(在边数限制在k条的条件下)。
if(dist[n]>0x3f3f3f3f/2) return -1;
else return dist[n];
详细注释
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510, M = 10010;
struct Edge {
int a;
int b;
int w;
} e[M];//把每个边保存下来即可
int dist[N];
int back[N];//备份数组防止串联
int n, m, k;//k代表最短路径最多包涵k条边
int bellman_ford() {
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < k; i++) {//k次循环
memcpy(back, dist, sizeof dist);
for (int j = 0; j < m; j++) {//遍历所有边
int a = e[j].a, b = e[j].b, w = e[j].w;
dist[b] = min(dist[b], back[a] + w);
//使用backup:避免给a更新后立马更新b, 这样b一次性最短路径就多了两条边出来
}
}
if (dist[n] > 0x3f3f3f3f / 2) return -1;
else return dist[n];
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < m; i++) {
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
e[i] = {a, b, w};
}
int res = bellman_ford();
if (res == -1) puts("impossible");
else cout << res;
return 0;
}
边无序,上一层状态会提前改变,滚动数组即可,一样的道理
看来同学对DP背包是有一定了解的
可以看看滚动数组咋写吗,大佬;
如果按正常的用树的存储方式存储,那么边就是有序的,是不是就不用备份数组了
求一个滚动数组得写法
改成这样就可以过了
强,我看的时候就想如果最短距离就是-1的时候怎么办
为什么改成这样就能过?
WA
总算理明白各种模版的优势了,谢谢老哥
这代码现在交了怎么过不了?
把res==-1给改成dist[n] > 0x3f3f3f3f / 2就可
请问为什么会这样啊
如果源点到终点的距离正好是-1,那么bellman会返回-1,输出impossible。但其实正确答案是-1.
Java把返回类型改为boolean,然后再在主函数判断一下输出impossible还是dist[n]。
可以理解为哈希碰撞,
最短路径
和用于标记无最短路径的数值
相撞哈哈哈,说的好
奶斯
哪个大佬能解释下注释里: “这样b一次性最短路径就多了两条边出来“?
没懂这句话的意思,根本目的不就是避免更新过程中受串联影响使得目标最短距离改变成可能比正确答案更小的值而出现错误吗??多了俩条边是什么意思呢??
歪瑞谷德
如果源点到终点的距离正好是-1,那么这个bellman_Ford会返回-1,主函数会输出impossible
别跟我狐闹~
这个我已经申请加强数据了,现在这个代码过不了了
改成-2就AC了
确实,接着找yy反应反应
直接设置函数为void然后再函数里面判断就AC了
if(dist[n]>0x3f3f3f3f/2) puts(“impossible”);
else printf(“%d”,dist[n]);
雀氏,这样就行
雀氏蟀啊,我都说雀氏蟀了,你二龙啊?
???雀食蟀
雀食蟀=确实帅 你不玩LOL的吗哈哈
我雀氏玩
你雀氏玩 你叫狐臭你不知道zjw的吗 他说的雀食蟀啊
学习了,没时间跟你狐闹了
别学了 大佬 学了一天还学
不行啊,我啥都不会啊
雀食蟀啊
特判一下不就得了
if(n==2&&m==1&&k==1&&a==1&&b==2&&w==-1){
puts(“-1”);
return 0;
}
6
你们两个是要笑死我啊
这个代码有一点小的问题,在函数中如果最后结果是无穷大,作者返回-1,刚才经过试验,数据中还真有一个就是结果是-1.哈哈哈,结果不输出-1,输出impossible。解决办法有两个一个是函数改成double,设置一个绝对不会出现的小数来判断。如下所示
另一个是在函数外面判断就没有问题,作者的一个小漏洞吧,各位注意!
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
using namespace std;
const int N = 505;
const int M = 10005;
int n,m,k;
int back[N],dist[N];
struct edge
{
int a,b,w;
}e[M];
int bellman()
{
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
}
int main()
{
cin>>n>>m>>k;
}
写的真好 用来复习很方便
为什么循环k次就能限制k条边呢?
没有#iinclude[HTML_REMOVED]为啥能比大小
dist[n]可能等于-1,这段代码要改一下
这里面的a b指的是啥
dist[n]>0x3f3f3f3f/2
为什么距离加和一定不会超过INF/2?有没有一种可能正好最终加和取到INF/2和INF之间,结果却判了不可达?
你看看题目范围。
如果无法从一号点走到n号点,不能返回-1了,因为dist[n]可能就是-1,本应该输出-1却输出了impossible。
这讲解太好了