852. spfa判断负环
给定一个 n
个点 m
条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数 n
和 m
。
接下来 m
行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z
。
输出格式
如果图中存在负权回路,则输出 Yes
,否则输出 No
。
数据范围
1≤n≤2000
,
1≤m≤10000,
图中涉及边长绝对值均不超过 10000
。
输入样例:
3 3
1 2 -1
2 3 4
3 1 -4
输出样例:
Yes
/**
* 松弛定理:d[u]+w < d[v] ;
*
* Bellman_Ford 算法:经过Nv-1 条边松弛,求得从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 一定是没有回路的,因为假设是正回路,那么去掉这条这条回路,一定
* 能得到更短的距离,如果是负回路,那么一定得不到最短路;
*
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于要判断是否有负权回路这种情况,即要进行 Nv-1 轮所有边的松弛操作,可以不使用pre数组,
* 因为进行Nv-1轮或者进行Nv轮迭代没什么区别,如果有负权值,那么进行Nv轮迭代以后边还是能够
* 松弛;
*/
/**
* 松弛定理:d[u]+w < d[v] ;
*
* Bellman_Ford 算法:经过Nv-1 条边松弛,求得从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 一定是没有回路的,因为假设是正回路,那么去掉这条这条回路,一定
* 能得到更短的距离,如果是负回路,那么一定得不到最短路;
*
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于要判断是否有负权回路这种情况,即要进行 Nv-1 轮所有边的松弛操作,可以不使用pre数组,
* 因为进行Nv-1轮或者进行Nv轮迭代没什么区别,如果有负权值,那么进行Nv轮迭代以后边还是能够
* 松弛;
*/
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
struct ENode
{
int v,w;
};
typedef pair<int,int> PII;
const int maxn = 2010 ,INF=1e9;
vector<ENode> Adj[maxn]; //邻接表int d[maxn];
int d[maxn]; //距离数组
bool hs[maxn]; //记录顶点是否被选择
int Nv,Ne; //顶点数,边数
void Read() //读入数据
{
cin >> Nv >> Ne;
for(int i=0;i<Ne;++i)
{
int u,v,w;
cin >> u >> v >> w;
Adj[u].push_back({v,w});
}
}
int Bellman_Ford(int st) //判断图中是否存在回路
{
fill(d,d+maxn,INF);
d[st]=0;
for(int k=1;k<Nv;++k)
{
for(int u=1;u<=Nv;++u)
for(int i=0;i<Adj[u].size();++i)
{
int v=Adj[u][i].v,w=Adj[u][i].w;
if(d[u]+w < d[v])
d[v]=d[u]+w;
}
}
for(int u=1;u<=Nv;++u)
for(int i=0;i<Adj[u].size();++i)
{
int v=Adj[u][i].v,w=Adj[u][i].w;
if(d[u]+w < d[v]) //还能被松弛,存在负回路
return 1;
}
return 0; //不存在负回路
}
int main()
{
Read();
if(Bellman_Ford(1))
puts("Yes");
else
puts("No");
return 0;
}
接下来的两个题目本来用SPFA算法会更好,但是我想尝试一下用Bellman_Ford算法解决,因此这两个题目我都是用的Bellman_Ford 算法解决的,下一篇博客我会用SPFA算法求解这两个题目。
853. 有边数限制的最短路
给定一个 n
个点 m
条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出从 1
号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n
号点,输出 impossible
。
注意:图中可能 存在负权回路 。
输入格式
第一行包含三个整数 n,m,k
。
接下来 m
行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z
。
点的编号为 1∼n
。
输出格式
输出一个整数,表示从 1
号点到 n 号点的最多经过 k
条边的最短距离。
如果不存在满足条件的路径,则输出 impossible
。
数据范围
1≤n,k≤500
,
1≤m≤10000,
1≤x,y≤n,
任意边长的绝对值不超过 10000
。
输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
* 松弛定理:d[u]+w < d[v] ;
*
* Bellman_Ford 算法:经过Nv-1 条边松弛,求得从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 一定是没有回路的,因为假设是正回路,那么去掉这条这条回路,一定
* 能得到更短的距离,如果是负回路,那么一定得不到最短路;
*
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于有边数k限制的图,在进行每一轮全部边松弛的时候,要将上一轮迭代的距离数组d拷贝到
* pre数组中,接下来进行边松弛的时候,对于边的起点距离需要用到pre数组,更新终点距离到
* d 数组;因为第一轮只能用到一条边,如果边的起点也用d数组,会使得更新了起点,而后面的
* 边因为起点被更新了,而这条边被松弛了 (即更新终点的距离),但实际情况是这条边不能被
* 松弛,仅仅是因为起点被更新了,而在判断终点的时候,没有使用原来的数组,而是用了被更新
* 的数组(能松弛终点这条边,应当还需要再加一条边才可以),画一个简单的图就能理解);
* 这是我画的一个图的数据:
* data :
* 4 4 1
1 2 2
2 3 3
2 4 4
4 3 1
/**
* 松弛定理:d[u]+w < d[v] ;
*
* Bellman_Ford 算法:经过Nv-1 条边松弛,求得从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 一定是没有回路的,因为假设是正回路,那么去掉这条这条回路,一定
* 能得到更短的距离,如果是负回路,那么一定得不到最短路;
*
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于有边数k限制的图,在进行每一轮全部边松弛的时候,要将上一轮迭代的距离数组d拷贝到
* pre数组中,接下来进行边松弛的时候,对于边的起点距离需要用到pre数组,更新终点距离到
* d 数组;因为第一轮只能用到一条边,如果边的起点也用d数组,会使得更新了起点,而后面的
* 边因为起点被更新了,而这条边被松弛了 (即更新终点的距离),但实际情况是这条边不能被
* 松弛,仅仅是因为起点被更新了,而在判断终点的时候,没有使用原来的数组,而是用了被更新
* 的数组(能松弛终点这条边,应当还需要再加一条边才可以),画一个简单的图就能理解);
* 这是我画的一个图的数据:
* data :
* 4 4 1
1 2 2
2 3 3
2 4 4
4 3 1
*/
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
struct ENode
{
int v,w;
};
const int maxn = 2010 ,INF=1e9;
vector<ENode> Adj[maxn]; //邻接表int d[maxn];
int d[maxn]; //距离数组
int pre[maxn];
bool hs[maxn]; //记录顶点是否被选择
int Nv,Ne; //顶点数,边数
int k;
void Read() //读入数据
{
cin >> Nv >> Ne >> k;
for(int i=0;i<Ne;++i)
{
int u,v,w;
cin >> u >> v >> w;
Adj[u].push_back({v,w});
}
}
void Bellman_Ford(int st)
{
fill(d,d+maxn,INF);
d[st]=0;
for(int i=1;i<=k;++i)
{
memcpy(pre,d,sizeof(d));
for(int u=1;u<=Nv;++u)
{
for(int j=0;j<Adj[u].size();++j)
{
int v=Adj[u][j].v,w=Adj[u][j].w;
if(pre[u]+w < d[v])
d[v]=pre[u]+w;
}
}
}
}
int main()
{
Read();
Bellman_Ford(1);
if(d[Nv] > INF/2) //由于有负权边,所以当两个点不能连通的时候,两个点的距离
puts("impossible"); //会小于INF
else
cout << d[Nv] << endl;
return 0;
}
851. spfa求最短路
给定一个 n
个点 m
条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出 1
号点到 n 号点的最短距离,如果无法从 1 号点走到 n
号点,则输出 impossible
。
数据保证不存在负权回路。
输入格式
第一行包含整数 n
和 m
。
接下来 m
行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z
。
输出格式
输出一个整数,表示 1
号点到 n
号点的最短距离。
如果路径不存在,则输出 impossible
。
数据范围
1≤n,m≤105
,
图中涉及边长绝对值均不超过 10000
。
输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2
/**
* Bellman_Ford 算法:经过Nv-1 条边松弛,从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于Bellnan_Ford 算法最坏时间复杂度是 O(NM),但是往往还未达到Nv-1轮松弛前就已经计算出
* 最短路径了,所以我们可以用一个 flag变量来标记此轮松弛操作是否该改变了dis数组的值,如果
* 没有改变,就提前退出,可以减少相当多的时间消耗;
* 我说怎么能AC,原来是保证不会存在负权回路,当有负权回路的时候,该算法一定是最坏时间复杂度;
*/
/**
* Bellman_Ford 算法:经过Nv-1 条边松弛,从源点到达各点的最短路;
* 为什么是 Nv-1 条边,因为Nv个没有回路的顶点的图,最多只有Nv-1条边;
* 如果进行Nv-1条边松弛以后,顶点还能被松弛,则一定存在负权回路;
* 因此Bellman_Ford 算法可以求解图是否有负权回路存在。
*
* 对于Bellnan_Ford 算法最坏时间复杂度是 O(NM),但是往往还未达到Nv-1轮松弛前就已经计算出
* 最短路径了,所以我们可以用一个 flag变量来标记此轮松弛操作是否该改变了dis数组的值,如果
* 没有改变,就提前退出,可以减少相当多的时间消耗;
* 我说怎么能AC,原来是保证不会存在负权回路,当有负权回路的时候,该算法一定是最坏时间复
* 杂度;
*/
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
struct ENode
{
int v,w;
};
typedef pair<int,int> PII;
const int maxn = 1e5+10 ,INF=1e9;
vector<ENode> Adj[maxn]; //邻接表int d[maxn];
int d[maxn]; //距离数组
bool hs[maxn]; //记录顶点是否被选择
int Nv,Ne; //顶点数,边数
void Read() //读入数据
{
cin >> Nv >> Ne;
for(int i=0;i<Ne;++i)
{
int u,v,w;
cin >> u >> v >> w;
Adj[u].push_back({v,w});
}
}
void Bellman_Ford(int st) //各点到st顶点的最短距离
{
fill(d,d+maxn,INF);
d[st]=0;
for(int k=1;k<Nv;++k)
{
int flag=0;
for(int u=1;u<=Nv;++u)
for(int i=0;i<Adj[u].size();++i)
{
int v=Adj[u][i].v,w=Adj[u][i].w;
if(d[u]+w < d[v])
{
d[v]=d[u]+w;
flag=1;
}
}
if(flag == 0)
break;
}
}
int main()
{
Read();
Bellman_Ford(1);
if(d[Nv] > INF/2)
puts("impossible");
else
cout << d[Nv] << endl;
return 0;
}