(图论)最短路问题合集(包含C,C++,Java,Python,Go)

不存在负权边:

1.朴素dijkstra算法

原题:

思路:(依然是贪心的思想)

1.初始化距离:dis[1]=0,dis[i]=INF(正无穷)

2.循环n次:

        找到当前不在s中的dis最小的点(s表示已经确定最短距离的点(可以开一个st数组表示))

        假设找到了t这个点,用这个点更新其他所有点的最短距离:

                if dis[x]>dis[t]+wi(这里wi表示边权)

实例演示:

代码如下:

一些注意细节(用//表示)

c++版本:

#include <iostream>
#include <cstring>

using namespace std;
const int N=510;
int  q[N][N];
int dis[N];
int n,m;
bool st[N];
int dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    for(int i=0;i<n-1;i++){

        int t=-1;
        for(int j=1;j<=n;j++){
            if(!st[j]&&(t==-1||dis[t]>dis[j])){
//这里t==-1,其实代表是第一次进入,更新t的值,而后面才开始比较
                t=j;
            }
        }
        for(int j=1;j<=n;j++){
            dis[j]=min(dis[j],dis[t]+q[t][j]);
        }
        st[t]=1;

    }
    if(dis[n]==0x3f3f3f3f) return -1;

    return dis[n];
}

int main(){
    cin>>n>>m;
    memset(q,0x3f,sizeof q);
    while(m--){
        int x,y,z;
        cin>>x>>y>>z;
        q[x][y]=min(q[x][y],z);
    }
    cout<<dijkstra()<<" ";
    return 0;
}

C:

#include <stdio.h>
#include <string.h>

#define N 510

int q[N][N];
int dis[N];
int n, m;
int st[N];

int min(int a, int b) {
    return a < b ? a : b;
}

int dijkstra() {
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < n - 1; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++) {
            if (!st[j] && (t == -1 || dis[t] > dis[j])) {
                t = j;
            }
        }
        for (int j = 1; j <= n; j++) {
            dis[j] = min(dis[j], dis[t] + q[t][j]);
        }
        st[t] = 1;
    }
    if (dis[n] == 0x3f3f3f3f) return -1;
    return dis[n];
}

int main() {
    scanf("%d %d", &n, &m);
    memset(q, 0x3f, sizeof(q));
    while (m--) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        q[x][y] = min(q[x][y], z);
    }
    printf("%d ", dijkstra());
    return 0;
}

 python:

import sys  
  
N = 510  
q = [[float('inf')] * N for _ in range(N)]  # 初始化邻接矩阵  
dis = [float('inf')] * N  # 初始化距离数组  
st = [False] * N  # 初始化标记数组  
  
n, m = map(int, input().split())  # 读取节点数和边数  
  
# 读取边的信息  
for _ in range(m):  
    x, y, z = map(int, input().split())  
    q[x - 1][y - 1] = min(q[x - 1][y - 1], z)  # 注意索引从0开始  
  
def dijkstra():  
    dis[0] = 0  # 起始节点距离设为0  
    for _ in range(n - 1):  
        t = -1  
        for j in range(n):  
            if not st[j] and (t == -1 or dis[j] < dis[t]):  
                t = j  
        for j in range(n):  
            if q[t][j] != float('inf'):  
                dis[j] = min(dis[j], dis[t] + q[t][j])  
        st[t] = True  
  
    if dis[n - 1] == float('inf'):  
        return -1  
    return dis[n - 1]  
  
print(dijkstra())

 Go:

package main  
  
import (  
	"bufio"  
	"fmt"  
	"math"  
	"os"  
	"strconv"  
	"strings"  
)  
  
const N = 510  
  
var (  
	q   [N][N]int  
	dis [N]int  
	n   int  
	m   int  
	st  = make([]bool, N)  
)  
  
func min(a, b int) int {  
	if a < b {  
		return a  
	}  
	return b  
}  
  
func dijkstra() int {  
	for i := range dis {  
		dis[i] = math.MaxInt32  
	}  
	dis[0] = 0 // 注意Go的数组索引从0开始  
  
	for i := 0; i < n; i++ {  
		t := -1  
		for j := 0; j < n; j++ {  
			if !st[j] && (t == -1 || dis[j] < dis[t]) {  
				t = j  
			}  
		}  
		for j := 0; j < n; j++ {  
			if q[t][j] != math.MaxInt32 {  
				dis[j] = min(dis[j], dis[t]+q[t][j])  
			}  
		}  
		st[t] = true  
	}  
  
	if dis[n-1] == math.MaxInt32 {  
		return -1 // 如果没有路径到达节点n-1  
	}  
  
	return dis[n-1]  
}  
  
func main() {  
	scanner := bufio.NewScanner(os.Stdin)  
  
	fmt.Scanln(&n, &m)  
  
	for i := range q {  
		for j := range q[i] {  
			q[i][j] = math.MaxInt32  
		}  
	}  
  
	for m > 0 {  
		m--  
		scanner.Scan()  
		line := scanner.Text()  
		fields := strings.Fields(line)  
  
		x, _ := strconv.Atoi(fields[0])  
		y, _ := strconv.Atoi(fields[1])  
		z, _ := strconv.Atoi(fields[2])  
		x-- // 转换为Go的索引  
		y--  
		q[x][y] = min(q[x][y], z)  
	}  
  
	fmt.Println(dijkstra())  
}

这里储存方式用邻接矩阵,主要是因为用于稠密图。图中可能存在重边和自环,但所有边权均为正值。算法复杂度:\mathcal{O}(n^2)

2.堆优化的dijkstra

我们思考一下,上述步骤在哪里可以优化:找到当前不在s中的dis最小的点,我们可以用堆进行优化,优化后复杂度为:\mathcal{O}(mlogn),堆优化,手写堆和优先队列,但是其实在dijkstra中,不需要手写堆,两个复杂度差不多,不如用优先队列方便。并且,此时为稀疏图,用邻接表更好。

 我们用邻接表现在只需要遍历邻接表中头元素连接的,进行更改,每一次取出队列中的最小值即可

C++:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e6 + 10;//注意开两倍大小
int h[N], w[N], e[N], ne[N], idx;
int n,m;
int dis[N];
bool st[N];
typedef pair<int,int> PII;
priority_queue<PII,vector<PII>,greater<PII>> p;
void add(int a, int b, int c)//模板,记下来就好了
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    p.push({0,1});
    while(p.size()){
        auto t=p.top();
        p.pop();
        int ver=t.second;
        if(st[ver]) continue;//判断是否之前更新过了
        st[ver]=1;
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>dis[ver]+w[i]){
                dis[j]=dis[ver]+w[i];
                p.push({dis[j],j});
            }
        }
    }
    if(dis[n]==0x3f3f3f3f) return -1;
    return dis[n];
}

int main(){
    cin>>n>>m;
    memset(h, -1, sizeof h);//邻接表记得初始化头结点
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    cout<<dijkstra()<<" ";
   return 0;
}

python:

import heapq

N = 1000010
h = [-1] * N
w = [0] * N
e = [0] * N
ne = [0] * N
idx = 0
n, m = map(int, input().split())
dis = [float('inf')] * N
st = [False] * N
p = []

def add(a, b, c):
    global idx
    e[idx] = b
    w[idx] = c
    ne[idx] = h[a]
    h[a] = idx
    idx += 1

def dijkstra():
    global dis
    dis[1] = 0
    heapq.heappush(p, (0, 1))
    while p:
        d, ver = heapq.heappop(p)
        if st[ver]:
            continue
        st[ver] = True
        i = h[ver]
        while i != -1:
            j = e[i]
            if dis[j] > dis[ver] + w[i]:
                dis[j] = dis[ver] + w[i]
                heapq.heappush(p, (dis[j], j))
            i = ne[i]
    if dis[n] == float('inf'):
        return -1
    return dis[n]

for _ in range(m):
    a, b, c = map(int, input().split())
    add(a, b, c)

print(dijkstra())

如果存在负权边:

3.bellman-ford

对于边的存储方式不高。故可以用结构体初始化。

方式:初始化所有点到源点的距离为∞,把源点到自己的距离设置为0,遍历n次;每次遍历m条边,用每一条边去更新各点到源点的距离。在碰到限制了最短路径上边的长度时就只能用bellman_ford了。

for n次
for 所有边 a,b,w (松弛操作)
dis[b] = min(dis[b],back[a] + w)

//注意:这里的backup非常重要,为了防止串联:(假设限制只能用1条边)

如下图:如果出现这样,不用之前的备份,就会出现1->3最近为2,而不是3,所以要备份一下之前的情况,用之前未更新的情况更新下一个节点。

 

c++: 

#include<iostream>
#include <cstring>

using namespace std;
const int N=510,M=10010;
struct edge{
    int a;
    int b;
    int w;
}edge[M];
int dis[N];
int backup[N];
int n,m,k;
void bellman_ford(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    for(int i=0;i<k;i++){
        memcpy(backup,dis,sizeof dis);
        for(int j=0;j<m;j++){
            auto e=edge[j];
            dis[e.b]=min(dis[e.b],backup[e.a]+e.w);
        }
    }
}

int main(){
    cin>>n>>m>>k;
    for(int i=0;i<m;i++){

        int x,y,z;
        cin>>x>>y>>z;
        edge[i]={x,y,z};
}
    bellman_ford();
    if(dis[n]>0x3f3f3f3f/2)  puts("impossible");//可能存在负权边
    else printf("%d\n", dis[n]);
    return 0;
}

c:

#include <stdio.h>
#include <string.h>

#define N 510
#define M 10010

struct Edge {
    int a;
    int b;
    int w;
};

struct Edge edge[M];
int dis[N];
int backup[N];
int n, m, k;

void bellman_ford() {
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;

    for (int i = 0; i < k; i++) {
        memcpy(backup, dis, sizeof dis);
        for (int j = 0; j < m; j++) {
            struct Edge e = edge[j];
            if (backup[e.a] + e.w < dis[e.b]) {
                dis[e.b] = backup[e.a] + e.w;
            }
        }
    }
}

int main() {
    scanf("%d %d %d", &n, &m, &k);

    for (int i = 0; i < m; i++) {
        int x, y, z;
        scanf("%d %d %d", &x, &y, &z);
        edge[i].a = x;
        edge[i].b = y;
        edge[i].w = z;
    }

    bellman_ford();

    if (dis[n] > 0x3f3f3f3f / 2) {
        printf("impossible\n");
    } else {
        printf("%d\n", dis[n]);
    }

    return 0;
}

python:

N = 510
M = 10010

class Edge:
    def __init__(self, a, b, w):
        self.a = a
        self.b = b
        self.w = w

edge = [Edge(0, 0, 0) for _ in range(M)]
dis = [float('inf')] * N
backup = [0] * N

def bellman_ford():
    global dis
    dis[1] = 0

    for _ in range(k):
        backup[:] = dis[:]
        for j in range(m):
            e = edge[j]
            dis[e.b] = min(dis[e.b], backup[e.a] + e.w)

def main():
    global n, m, k
    n, m, k = map(int, input().split())

    for i in range(m):
        x, y, z = map(int, input().split())
        edge[i] = Edge(x, y, z)

    bellman_ford()

    if dis[n] > 0x3f3f3f3f // 2:
        print("impossible")
    else:
        print(dis[n])

if __name__ == "__main__":
    main()

 Go:

package main

import (
	"fmt"
)

const N = 510
const M = 10010

type Edge struct {
	a, b, w int
}

var edge [M]Edge
var dis [N]int
var backup [N]int
var n, m, k int

func bellmanFord() {
	for i := range dis {
		dis[i] = 0x3f3f3f3f
	}
	dis[1] = 0

	for i := 0; i < k; i++ {
		copy(backup[:], dis[:])
		for j := 0; j < m; j++ {
			e := edge[j]
			if dis[e.b] > backup[e.a]+e.w {
				dis[e.b] = backup[e.a] + e.w
			}
		}
	}
}

func main() {
	fmt.Scan(&n, &m, &k)

	for i := 0; i < m; i++ {
		var x, y, z int
		fmt.Scan(&x, &y, &z)
		edge[i] = Edge{x, y, z}
	}

	bellmanFord()

	if dis[n] > 0x3f3f3f3f/2 {
		fmt.Println("impossible")
	} else {
		fmt.Println(dis[n])
	}
}

 时间复杂度:\mathcal{O}(mn)

4.spfa

对bellman-ford的优化,不一定每条边都会更新(spfa算法的想法基础)。

dis[b] = min(dis[b],back[a] + w)

观察这个式子,只有back[a]变小了,我的后继dis[b]才会变小

所以,我可以用一个队列,在一次变化中,只要有节点变小了,那么就肯定会影响后继节点,就放入队列之中。只要队列不空,就一直类似于bfs一样进行。

 时间复杂度:一般\mathcal{O}(m),最坏\mathcal{O}(mn)

//与dijkstra非常相似
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dis[N];
bool st[N];
queue<int> q;
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int spfa(){
    memset(dis,0x3f,sizeof dis);
    dis[1]=0;
    q.push(1);
    st[1]=1;
    while(q.size()){
        auto t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>dis[t]+w[i]){
                dis[j]=dis[t]+w[i];
                if(!st[j]){
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
return dis[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 = spfa();

    if (t == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", t);

    return 0;
}

 c:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdbool.h>  
  
#define N 100010  
#define INF 0x3f3f3f3f  
  
int n, m;  
int h[N], w[N], e[N], ne[N], idx;  
int dis[N];  
bool st[N];  
  
typedef struct {  
    int data;  
} QueueNode;  
  
typedef struct {  
    QueueNode q[N];  
    int front, rear;  
} Queue;  
  
void initQueue(Queue *q) {  
    q->front = q->rear = 0;  
}  
  
bool isEmpty(Queue *q) {  
    return q->front == q->rear;  
}  
  
void enqueue(Queue *q, int x) {  
    q->q[q->rear].data = x;  
    q->rear = (q->rear + 1) % N; // 使用循环队列防止溢出  
}  
  
int dequeue(Queue *q) {  
    if (isEmpty(q)) return -1; // 队列为空,返回错误标识  
    int x = q->q[q->front].data;  
    q->front = (q->front + 1) % N;  
    return x;  
}  
  
void add(int a, int b, int c) {  
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;  
}  
  
int spfa() {  
    memset(dis, INF, sizeof(dis));  
    dis[1] = 0;  
    Queue q;  
    initQueue(&q);  
    enqueue(&q, 1);  
    st[1] = true;  
  
    while (!isEmpty(&q)) {  
        int t = dequeue(&q);  
        st[t] = false;  
        for (int i = h[t]; i != -1; i = ne[i]) {  
            int j = e[i];  
            if (dis[j] > dis[t] + w[i]) {  
                dis[j] = dis[t] + w[i];  
                if (!st[j]) {  
                    enqueue(&q, j);  
                    st[j] = true;  
                }  
            }  
        }  
    }  
    return dis[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 = spfa();  
  
    if (t == INF) printf("impossible\n");  
    else printf("%d\n", t);  
  
    return 0;  
}

 python:

from collections import deque

N = 100010

n, m = map(int, input().split())
h = [-1] * N
w = [0] * N
e = [0] * N
ne = [0] * N
dis = [float('inf')] * N
st = [False] * N
q = deque()

def add(a, b, c):
    global idx
    e[idx] = b
    w[idx] = c
    ne[idx] = h[a]
    h[a] = idx
    idx += 1

def spfa():
    global dis
    dis[1] = 0
    q.append(1)
    st[1] = True

    while q:
        t = q.popleft()
        st[t] = False
        i = h[t]
        while i != -1:
            j = e[i]
            if dis[j] > dis[t] + w[i]:
                dis[j] = dis[t] + w[i]
                if not st[j]:
                    q.append(j)
                    st[j] = True
            i = ne[i]

    return dis[n]

idx = 0
for _ in range(m):
    a, b, c = map(int, input().split())
    add(a, b, c)

t = spfa()

if t == float('inf'):
    print("impossible")
else:
    print(t)

5.spfa拓展:判断负环

原理:鸽笼原理+三角不等式

使用spfa算法解决是否存在负环问题

求负环的常用方法,基于SPFA,一般都用方法 2(该题也是用方法 2):

方法 1:统计每个点入队的次数,如果某个点入队n次,则说明存在负环
方法 2:统计当前每个点的最短路中所包含的边数,如果某点的最短路所包含的边数大于等于n,则也说明存在环

每次做一遍spfa()一定是正确的,但时间复杂度较高,可能会超时。初始时将所有点插入队列中可以按如下方式理解:
在原图的基础上新建一个虚拟源点,从该点向其他所有点连一条权值为0的有向边。那么原图有负环等价于新图有负环。此时在新图上做spfa,将虚拟源点加入队列中。然后进行spfa的第一次迭代,这时会将所有点的距离更新并将所有点插入队列中。执行到这一步,就等价于视频中的做法了。那么视频中的做法可以找到负环,等价于这次spfa可以找到负环,等价于新图有负环,等价于原图有负环。得证。

1、dist[x] 记录虚拟源点到x的最短距离

2、cnt[x] 记录当前x点到虚拟源点最短路的边数,初始每个点到虚拟源点的距离为0,只要他能再走n步,即cnt[x] >= n,则表示该图中一定存在负环,由于从虚拟源点到x至少经过n条边时,则说明图中至少有n + 1个点,表示一定有点是重复使用

3、若dist[j] > dist[t] + w[i],则表示从t点走到j点能够让权值变少,因此进行对该点j进行更新,并且对应cnt[j] = cnt[t] + 1,往前走一步

注意:该题是判断是否存在负环,并非判断是否存在从1开始的负环,因此需要将所有的点都加入队列中,更新周围的点

引入一个cnt数组,记录每个点经过的边数 

 e.g.

 但是,如果从1开始到不了负环地方,那么就会出问题,我们的解决方法是一开始把所有的点都放入队列中:(本质就是以每个点为起点做一遍spfa)

for(int i=1;i<=n;i++){
    st[i]=1;
    q.push(i);
}

 需要再cnt基础上更改的地方:

 dis[j]=dis[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;

还有对于cnt数组的初始化,还有把spfa变成布尔函数

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dis[N];
int cnt[N];
bool st[N];
queue<int> q;
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

bool spfa(){
    memset(dis,0x3f,sizeof dis);
for(int i=1;i<=n;i++){
    st[i]=1;
    q.push(i);
}

    dis[1]=0;
    q.push(1);
    st[1]=1;
    while(q.size()){
        auto t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(dis[j]>dis[t]+w[i]){
                dis[j]=dis[t]+w[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                if(!st[j]){
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
return false;
}
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);
    }
if(spfa()) puts("Yes");
else puts("No");

    return 0;
}

多源汇最短路问题:

6.Floyd算法

原题:

原理:基于动态规划:

d[k,i,j]表示从第i个点出发到达j,只经过1~k个点的最短距离

状态转移方程:d[k,i,j]=d[k-1,i,k]+d[k-1,k,j]

发现:k与k-1刚好可以消去这个维度,用一个数组就可以实现

d[i,j]=d[i,k]+d[k,j]

算法时间复杂度:\mathcal{O}(n^3)

具体:

假设节点序号是从1到n。
    假设f[0][i][j]是一个n*n的矩阵,第i行第j列代表从i到j的权值,如果i到j有边,那么其值就为ci,j(边ij的权值)。
    如果没有边,那么其值就为无穷大。

    f[k][i][j]代表(k的取值范围是从1到n),在考虑了从1到k的节点作为中间经过的节点时,从i到j的最短路径的长度。

    比如,f[1][i][j]就代表了,在考虑了1节点作为中间经过的节点时,从i到j的最短路径的长度。
    分析可知,f[1][i][j]的值无非就是两种情况,而现在需要分析的路径也无非两种情况,i->j,i->1->j:
    【1】f[0][i][j]:i->j这种路径的长度,小于,i->1->j这种路径的长度
    【2】f[0][i][1]+f[0][1][j]:i->1->j这种路径的长度,小于,i->j这种路径的长度


    形式化说明如下:
    f[k][i][j]可以从两种情况转移而来:
    【1】从f[k−1][i][j]转移而来,表示i到j的最短路径不经过k这个节点
    【2】从f[k−1][i][k]+f[k−1][k][j]转移而来,表示i到j的最短路径经过k这个节点

    总结就是:f[k][i][j]=min(f[k−1][i][j],f[k−1][i][k]+f[k−1][k][j])
    从总结上来看,发现f[k]只可能与f[k−1]有关。

初始化与读入邻接矩阵(存在自环和重边的时候):

for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= n; j ++ )
        if (i == j) d[i][j] = 0;
        else d[i][j] = INF;

while (m -- )
{
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    d[a][b] = min(d[a][b], c);
}

c++:

#include <iostream>
using namespace std;


const int N = 210, INF = 1e9;

int n, m, Q;
int d[N][N];
void floyd(){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d", &n, &m, &Q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        d[a][b] = min(d[a][b], c);
    }

    floyd();

    while (Q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);

        int t = d[a][b];
        if (t > INF / 2) puts("impossible");
        else printf("%d\n", t);
    }

    return 0;
}

c:

#include <stdio.h>

#define N 210
#define INF 1000000000

int n, m, Q;
int d[N][N];

void floyd() {
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (d[i][k] < INF && d[k][j] < INF) {
                    int new_dist = d[i][k] + d[k][j];
                    if (new_dist < d[i][j]) {
                        d[i][j] = new_dist;
                    }
                }
            }
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &Q);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j) {
                d[i][j] = 0;
            } else {
                d[i][j] = INF;
            }
        }
    }

    while (m--) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        if (c < d[a][b]) {
            d[a][b] = c;
        }
    }

    floyd();

    while (Q--) {
        int a, b;
        scanf("%d%d", &a, &b);

        int t = d[a][b];
        if (t > INF / 2) {
            puts("impossible");
        } else {
            printf("%d\n", t);
        }
    }

    return 0;
}

java:

import java.util.Scanner;

public class Main {
    static final int N = 210;
    static final int INF = 1000000000;

    static int n, m, Q;
    static int[][] d = new int[N][N];

    public static void floyd() {
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]);
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        n = scanner.nextInt();
        m = scanner.nextInt();
        Q = scanner.nextInt();

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i == j) d[i][j] = 0;
                else d[i][j] = INF;
            }
        }

        while (m-- > 0) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            int c = scanner.nextInt();
            d[a][b] = Math.min(d[a][b], c);
        }

        floyd();

        while (Q-- > 0) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();

            int t = d[a][b];
            if (t > INF / 2) {
                System.out.println("impossible");
            } else {
                System.out.println(t);
            }
        }

        scanner.close();
    }
}

python:

import sys

N = 210
INF = 10**9

def floyd():
    global n, d
    for k in range(1, n+1):
        for i in range(1, n+1):
            for j in range(1, n+1):
                d[i][j] = min(d[i][j], d[i][k] + d[k][j])

if __name__ == "__main__":
    n, m, Q = map(int, input().split())

    d = [[INF for _ in range(N)] for _ in range(N)]

    for i in range(1, n+1):
        d[i][i] = 0

    for _ in range(m):
        a, b, c = map(int, input().split())
        d[a][b] = min(d[a][b], c)

    floyd()

    for _ in range(Q):
        a, b = map(int, input().split())

        t = d[a][b]
        if t > INF // 2:
            print("impossible")
        else:
            print(t)

Go语言:

package main

import "fmt"

const N = 210
const INF = 1000000000

var n, m, Q int
var d [N][N]int

func floyd() {
	for k := 1; k <= n; k++ {
		for i := 1; i <= n; i++ {
			for j := 1; j <= n; j++ {
				if d[i][k] < INF && d[k][j] < INF {
					newDist := d[i][k] + d[k][j]
					if newDist < d[i][j] {
						d[i][j] = newDist
					}
				}
			}
		}
	}
}

func main() {
	fmt.Scanf("%d%d%d", &n, &m, &Q)

	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			if i == j {
				d[i][j] = 0
			} else {
				d[i][j] = INF
			}
		}
	}

	for m > 0 {
		var a, b, c int
		fmt.Scanf("%d%d%d", &a, &b, &c)
		if c < d[a][b] {
			d[a][b] = c
		}
		m--
	}

	floyd()

	for Q > 0 {
		var a, b int
		fmt.Scanf("%d%d", &a, &b)

		t := d[a][b]
		if t > INF/2 {
			fmt.Println("impossible")
		} else {
			fmt.Println(t)
		}
		Q--
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/601141.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

three.js 效果细节提升

1. three.js 效果细节提升 加载模型时&#xff0c;给模型设置接受阴影&#xff0c;反射阴影 gltfLoader.load("./model/court-transformed.glb", (gltf) > {gltf.scene.traverse(child > {if (child.isMesh) {child.castShadow true; // 设置阴影可以投射阴…

c++笔记——概述运算符重载——解析运算符重载的难点

前言:运算符重载是面向对象的一个重要的知识点。我们都知道内置类型可以进行一般的运算符的运算。但是如果是一个自定义类型&#xff0c; 这些运算符就无法使用了。那么为了解决这个问题&#xff0c; 我们的祖师爷就在c中添加了运算符重载的概念。 本篇主要通过实例的实现来讲述…

【时序大模型总结】学习记录(1)

1.TimeGPT-1 思路&#xff1a;在来自不同领域的大量数据上训练模型&#xff0c;然后对未见过的数据产生零样本的推断。 作者对TimeGPT进行了超过1000亿个数据点的训练&#xff0c;这些数据点都来自开源的时间序列数据。该数据集涵盖了广泛的领域&#xff0c;从金融、经济和天气…

YOLOv8原理解析[目标检测理论篇]

接下来是我最想要分享的内容&#xff0c;梳理了YOLOv8预测的整个流程&#xff0c;以及训练的整个流程。 关于YOLOv8的主干网络在YOLOv8网络结构介绍-CSDN博客介绍了&#xff0c;为了更好地介绍本章内容&#xff0c;还是把YOLOv8网络结构图放在这里&#xff0c;方便查看。 1.YOL…

AI讲师大模型培训老师叶梓:大模型应用的方向探讨

大模型应用的关键方向及其落地案例可以从多个角度进行探讨&#xff0c;结合最新的研究和实际应用案例&#xff0c;我们可以更全面地理解这些技术如何推动社会和经济的发展。 Agent&#xff08;数字代理&#xff09;: 方向说明:Agent方向的AI技术旨在创建能够独立执行任务、做出…

对于SOMP算法的测试

刚开始只上传了SOMP算法的代码&#xff0c;并没有过多介绍。 所以本篇文章对SOMP算法用法进行一个介绍 SOMP算法代码 function [X_hat] MMV_SOMP(Y, PHI, s)% SOMP:同时正交匹配追踪 simultaneous orthogonal matching pursuit% 论文&#xff1a;J. Determe, J. Lo…

若依plus 某些接口(用户信息等)响应突然变慢

今天一大早起来发现我的接口突然响应变慢了&#xff01; 就什么都没动&#xff0c;啥也没改&#xff0c;但是一些接口又很快。 百度了很多&#xff0c;都说叫我改sql查询方式&#xff0c;又怀疑是过滤器的问题&#xff0c;很遗憾都不是&#xff01; 一个响应40秒&#xff01;…

[译文] 恶意代码分析:1.您记事本中的内容是什么?受感染的文本编辑器notepad++

这是作者新开的一个专栏&#xff0c;主要翻译国外知名安全厂商的技术报告和安全技术&#xff0c;了解它们的前沿技术&#xff0c;学习它们威胁溯源和恶意代码分析的方法&#xff0c;希望对您有所帮助。当然&#xff0c;由于作者英语有限&#xff0c;会借助LLM进行校验和润色&am…

IOT-9608I-L ADC端口的使用(连续采样ADC值)

目录 概述 1 硬件介绍 1.1 认识硬件 1.2 引脚信号定义 2 软件功能实现 2.1 查看iio:device0下的接口信息 2.2 实现连续采样ADC 2.2.1 功能描述 2.2.2 代码实现 2.2.3 详细代码 3 测试 概述 本文主要讲述IOT-9608I-L ADC端口的使用方便&#xff0c;其内容包括板卡上的…

密室逃脱游戏-第12届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第58讲。 密室逃脱游戏&…

2024年第九届数维杯数学建模B题思路分享

文章目录 1 赛题思路2 比赛日期和时间3 竞赛信息4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

分布式模式让业务更高效、更安全、更稳定

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#x1f525;&#xff1a;探索设计模式的魅力&#xff1a;分布式模…

Ubuntu添加网络映射路径

参考资料 linux挂在阿里云盘&#xff08;webdav协议&#xff09;给服务器扩容、备份数据等_davfs2-CSDN博客 Linux将WebDAV为本地磁盘 - 夏日冰菓 (lincloud.pro) systemd系统开机运行rc.local_rc-local.service: failed to execute command: exec -CSDN博客 系统版本&#xff…

word格式技巧

文章目录 论文格式技巧论文交叉引用怎么弄论文的页码怎么弄 论文格式技巧 论文交叉引用怎么弄 1.取消文献原有的编号 2.定义新编号 3.具体编号设置 4.在引用的地方插入&#xff0c;具体引用选项卡–>交叉引用–>选择后插入 2. 4. 论文的页码怎么弄 假设我们有这样一…

List的两种实现

前置知识&#xff1a; 数组 baseAddress&#xff1a;数组的首地址 dataTypeSize&#xff1a;数组中元素类型的大小&#xff0c;如int为4字节 为什么数组索引从0开始&#xff0c;假如从1开始不行吗&#xff1f; 在根据数组索引获取元素的时候&#xff0c;会用索引和寻址公式来计…

HBase 读写流程

HBase 读写流程 1. 读流程 Client先访问zookeeper&#xff0c;从zookeeper获取meta region的位置从meta region中读取meta表中的数据&#xff0c;meta中存储了用户表的region信息&#xff1b;根据namespace、表名和rowkey在meta表中找到对应的region信息&#xff1b;找到这个r…

[Kotlin]创建一个私有包并使用

1.创建Kotlin项目 创建项目&#xff1a; 在Android Studio或其他IDE中选择“Create New Project”。选择Kotlin和Gradle作为项目类型和构建系统。指定项目名称和位置&#xff0c;完成设置。 添加依赖: 如果你的库需要额外的依赖&#xff0c;可以在 build.gradle (Module: app…

文件各种上传,离不开的表单 [html5]

作为程序员的我们&#xff0c;经常会要用到文件的上传和下载功能。到了需要用的时候&#xff0c;各种查资料。有木有..有木有...。为了方便下次使用&#xff0c;这里来做个总结和备忘。 利用表单实现文件上传 最原始、最简单、最粗暴的文件上传。 前端代码&#xff1a; //方…

oracle 清理 trace 和 alert 日志文件

某天,发现磁盘空间被占满了&#xff0c;继续查询发现是 oracle 的日志文件占满了磁盘空间 其中: trace文件有35G, alert 有23G 目录地址是: diag/rdbms/orcl/orcl/trace, diag/rdbms/orcl/orcl/alert 都是在 oracle 目录下的 diag 目录内部 # 可以使用 以下命令对目录大小进行排…

Git与GitHub交互

注册 https://github.com/ 本地库与远程库交互方式 创建本地库并提交文件 创建远程库 在本地库创建远程库地址别名 查看现有远程库地址的别名 git remote -v 创建远程库地址别名 git remote add [别名] [远程地址] 远程路地址位置 示例 成员1推送 git push [别名] [分支…
最新文章