#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
bool topsort(){
int hh =0, tt = -1;
for(int i = 1; i <= n; i++){
if(!d[i]){
q[ ++tt] = i;
}
}
while(hh <= tt){
int t = q[hh ++];
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
d[j] --;
if(d[j] == 0) q[ ++tt] = j;
}
}
//tt = n - 1说明所有的点都进入过队列了,也就说明有拓扑序列
return tt == n -1;
}
int main(){
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i = 0; i < m; i++){
int a, b;
cin >> a >> b;
add(a, b);
d[b] ++;
}
if(topsort()){
//我们还可以发现出队的顺序就是拓扑序列
for(int i = 0; i < n; i ++) {
printf("%d ", q[i]);
// puts("");
}
}
else{
puts("-1");
}
}
最短路问题
1,单源最短路
1,所有边权都是正数 ----- 朴素的Djkstra算法,堆优化的Dijkstra算法
2,存在负边权 ----- Bellman -Ford, SPFA
2,多源汇最短路
Floyd算法
最短路问题 ----- 朴素Dijkstra算法
1,设初始化距离,dist数组,dist[ 1 ] = 0也就是1号点到起始点距离为0,其余点dist[ i ] = +∞
2,for(i = 0 ; i < n; i ++),现在有个集合s,里面放着当前已经确定了最短距离的点
1),我们现在循环中找到第一个不在s中的距离最近的点t
2),用t来更新其他点的距离,更新的方式是从 t 出去的所有边能都
cpp复制代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra(){
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for(int i = 0; i < n; i++){
int t = -1;//当前走到的距离最近的点,等于-1时说明还没开始延申点
for(int j = 1; j <= n;j ++){
//如果这个点没走过,并且t=-1或者当前有更短的路可以走另一个节点,就换t
//这一步就是在找我们走哪个路比较近
if(!st[j] && (t == -1 || dist[t] > dist[j])){
t = j;
}
}
st[t] = true;
//迭代n次,然后每次就是从我们现在的最近的点t来找之后的路
for(int j = 1; j <= n; j++){
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main(){
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
while(m --){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = min(g[a][b], c);
}
int t = dijkstra();
cout<<t;
}
最短路问题 ----- 堆优化Dijkstra算法
我们朴素排序虽然看着简单,但是如果节点数到了1e4我们就包超时的,所以我们有了堆优化的方法
我们用一个堆来维护最短的距离和节点
cpp复制代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 3e5 + 10;
typedef pair<int, int> PII;
int n, m;
int h[N], e[N], ne[N], idx, w[N];
int dist[N];
bool st[N];
void add(int a, int b, int c){
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx ++;
}
int dijkstra(){
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII> > heap;
heap.push({0, 1});
while(heap.size()){
auto 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});
}
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main(){
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while(m --){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
int t = dijkstra();
cout<<t;
}
Bellman-Ford算法
这就比较暴力了,就直接把边存在一个结构体里,然后遍历,然后看题上有没有用限制边的个数,没的话就迭代 n 次,然后去找最短路,有限制,就迭代 k 条边,k次
具体看代码就行
cpp复制代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510, M = 10010;
int n, m, k;
int dist[N], backup[N];//备份dist数组
struct Edge{
int a, b, w;
}edges[M];
int Bellman_ford(){
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
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);
}
}
//防止最后一个节点的的前一个边时负值
if(dist[n] > (0x3f3f3f3f / 2)) return -1;
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);
edges[i] = {a, b, w};
}
int t = Bellman_ford();
if(t == -1) puts("impossible");
else{
printf("%d", t);
}
}