第三章 搜索与图论(二)——最短路问题

news2024/9/20 6:40:11

文章目录

    • 单源最短路
      • 朴素Dijkstra
      • 堆优化版Dijkstra
      • Bellman Ford算法
      • SPFA
        • SPFA求负环
    • 多源汇最短路
      • Floyd
    • 最短路练习题
      • 849. Dijkstra求最短路 I
      • 850. Dijkstra求最短路 II
      • 853. 有边数限制的最短路
      • 851. spfa求最短路
      • 852. spfa判断负环
      • 854. Floyd求最短路

源点表示起点,汇点表示终点
image.png
一些认识:
m和 n 2 n^2 n2一个级别是稠密图,m和n一个级别是稀疏图
最短路问题不区分有向图与无向图,因为无向图是一种特殊的有向图,能解决有向图的最短路问题,就能解决无向图的最短路问题

单源最短路

起点确定,终点是除起点外的其他点
默认n表示点数,m表示边数

  1. 所有边权为正数
  • 朴素Dijkstra O( n 2 n^2 n2),和边数无关,用于稠密图
  • 堆优化版Dijkstra O(mlogn) m = n 2 n^2 n2,用于稀疏图
  1. 边权存在负数
  • Bellman-Ford O(nm)
  • SPFA O(m),最坏O(nm),是Bellman-Ford的优化

朴素Dijkstra

基于贪心思想,每次选择距离源点最近的点

维护一个s集合,存储已经确定最短路径的点
一开始该集合中只有源点,不断地将点加入到s集合中,直到图中所有点都属于s集合,最短路求解结束

如何将点加入s集合?

  1. 遍历图中不属于s的点,选择其中与源点距离最小的点加入s
  2. 对于此时不属于s的点,用新加入s的点尝试更新其他点的最短距离

重复以上两个步骤,直到图中所有点都属于s
如何用代码实现?
dis数组存储每个点与源点的最短距离,初始化:源点到源点的距离为0
g数组为邻接矩阵,存储图中每条边的权值(若是稀疏图则使用邻接表
vt数组用来维护s集合,存储该点是否在s中的bool值

  1. 初始化dis[源点的编号] = 0, dis[其他点] = +∞
  2. 遍历dis数组不属于s的点,选择其中与源点距离最短的点加入s
  3. 根据新加入s的点,更新dis数组中其他不属于s的点,假设新加入点的编号为x,`dis[y] = min(dis[y], dis[x] + g[x][y])
  4. 重复2,3两个步骤,直到所有点属于s

第三步中,对于已经确定最短路径的点(属于s中的点) ,需要根据该点更新其他不属于s中的点。此时进行操作dis[y] = min(dis[y], dis[x] + g[x][y]),其实不需要特别要求该点不属于s,因为属于s的点已经确定了最短距离,不论是否进行min操作都不影响已经确定的最短距离。所以在min操作之前判断该点是否属于s的操作是无关紧要的

模板:

bool vt[N];
int dis[N], g[x][y];

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    
    for (int i = 0; i < n; ++ i )
    {
        int x = -1;
        for (int j = 1; j <= n; ++ j)
            if (!vt[j] && (x == -1 || dis[j] < dis[x])) 
                x = j;

        vt[x] = true; // 从不属于s的点中选择距离源点最近的点
        for (int y = 1; y <= n; ++ y) // 用新加入的点更新其他点
            dis[y] = min(dis[y], dis[x] + g[x][y]);
    }
}

外循环迭代n次,每次选择与源点距离最近的点放入集合中,集合中的点为已经确定最短距离的点。迭代n次后,图中所有点都进入了s集合,即确定了所有点的最短距离
每一次迭代要做的事:

  1. 在不属于s中的点中,找到与源点距离最近的点
  2. 用该点更新其他(不属于s的)点

堆优化版Dijkstra

用堆优化朴素Dijkstra算法,时间复杂度可以达到O(mlogn)
若手写一个支持修改任意位置的堆,空间复杂度为O(n)
若使用优先队列,空间复杂度将达到O(m),存储稠密图比较浪费空间

朴素Dijkstra算法中,在不属于s的点中,找距离源点最近的点,时间复杂度为O(n)
外循环需要迭代n次,所以总得时间复杂度为O( n 2 n^2 n2),这是朴素Dijkstra算法的效率瓶颈
用堆结构对朴素算法进行优化,我们能以O(1) 的时间取出距离源点最近的点,不过代价就是提高了空间复杂度,从O(n) 提高到了O(m)。以及每次维护堆时,时间复杂度为O(logn)

同时,若使用邻接矩阵存储边,用新加入的点更新其他点时,时间复杂度为O(n),总的时间复杂度为O( n 2 n^2 n2)。也是一个瓶颈,不过很好解决,用邻接表存储边,总的时间复杂度为O(m)

而使用邻接表存储,不用考虑重边问题。邻接矩阵只能存储两点间的一条边,所以要选择重边中最小的边

代码如何实现?
用优先队列存储pairfirst为点到源点的距离,second为点的编号
由于优先队列无法修改元素,所以图中的每条边都会进入一次优先队列,此时存在冗余且无效的数据
具体情况是:队列中存在second相同的pair,此时first较大的pair为无效数据。但我们无法删除任意位置的pair,只能删除堆顶的pair。所以取出堆顶元素时需要判断该pairsecond是否已经在s中(最短距离是否已经确定
若已经在s中,说明不需要进行接下来的操作(加入s和用该点更新其他点),所以此时直接continue

模板:

typedef pair<int, int> PII;

int h[N], e[N], ne[N], w[N], idx = 1;
priority_queue<PII, vector<PII>, greater<PII>> q;
int dis[N];
bool vt[N];

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    q.push({ 0, 1 }), dis[1] = 0;
    
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        
        int x = t.second, d = t.first;
        if (vt[x]) continue;
        vt[x] = true;
        
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!vt[y] && d + w[i] < dis[y])
            {
                dis[y] = d + w[i];
                q.push({ dis[y], y });
            }
        }
    }
}

Bellman Ford算法

暴力美学

用于处理负权边
存储边的结构没有要求,可以用简单的结构体存储

struct Edge
{
	int x, y, w;
}edges[M];

循环n次,每次都遍历所有边
遍历某条边时,对其进行松弛操作:dis[y] = min(dis[y], dis[x] + w[i]),其中x是当前边的起点,y是当前边的终点,w[i]是当前边的权重
每次的松弛操作其实是在尝试更新dis[y],而更新依赖于dis[x]。若dis[x]不是无穷大,而是被其他松弛操作更新了,那么dis[y]也可以被当前松弛操作更新

外循环与dis数组的关系:若循环进行了k次,dis数组存储从源点开始走,不超过k条边,递达其他点的最短距离

若存在负环,则最短路不一定存在(负环与源点和目标点不连通时则最短路存在
用Bellman-Ford判断负环:
第n次外循环时,若更新了dis数组,说明某条最短路中有n条边,即n+1个点,根据抽屉原理,该图存在负环
但Bellman-Ford的时间复杂度较高,一般用SPFA解决负环问题

三角不等式:dis[y] <= dis[x] + w

串联问题:每次对所有边进行松弛操作时,应该基于上一次对所有边进行松弛操作后的状态。什么意思呢?对所有边进行了一次松弛操作后,我们要备份此时的dis数组,作为上一次的状态保存
若不备份dis数组,而直接进行松弛操作。修改dis数组的某些数据后,此时的dis数组不再是上一次对所有边进行松弛操作后的状态,基于这个状态进行松弛操作得到的结果多半是错误的

串联问题与dp状态压缩导致的问题类似

模板:

struct Edge
{
	int x, y, w;
}edges[M];

int dis[N], bup[N];
void bellman_ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto t = edges[j];
            dis[t.y] = min(dis[t.y], bup[t.x] + t.w);
        }
    }
}

SPFA

SPFA是对Bellman-Ford算法的一个优化
注意松弛操作:dis[y] = min(dis[y], dis[x] + w[i]) 什么时候这个操作才是有效的?只有当dis[x]变小,dis[y]才可能变小 当dis[x]不变时,dis[y]`一定不会变换,所以Bellman-Ford中,大部分的更新多余且无效

如何使得每次的更新都是有效的呢?
运用广搜的思想,将每次更新(有效松弛)的点进入队列
初始时,将源点入队,表示源点的最短路被更新
每次取出队头的点,对连接该点的所有边进行松弛操作。通过邻接表将与该点邻接的点入队,因为这些点的dis距离被更新了
直到队列为空,此时整张图更新完成

注意,为防止一个点的重复地进入队列,要维护每个点的状态:某个点入队之前,先判断该点是否已经在队列中

模板:

// N为点数,M为边数
int dis[N];
int h[N], e[M], ne[M], w[M], idx = 1;
int q[N], hh, tt = -1;
bool st[N];

void add(int x, int y, int d)
{
	e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa()
{
	memset(dis, 0x3f, sizeof(dis));
	q[++ tt] = 1, st[1] = true, dis[1] = 0; // 假设1号点为源点
	while (tt >= hh)
	{
		int x = q[hh ++ ];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i] )
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y]) 
			{
				dis[y] = dis[x] + w[i];
				if (!st[y]) st[y] = true, q[++ tt ] = y;
			}
		}
	}
}

出题者可能故意卡SPFA的时间,使时间复杂度达到最坏的情况O(nm)
此时只能使用其他算法

SPFA求负环

和Bellman-Ford一样,SPFA也是运用抽屉原理判断图中是否有负环

在SPFA求负环的算法中,虽然求解过程与原SPFA差不多,但是有些思想是完全不一样的
比如dis数组不再表示其他点到源点的距离,可以认为该数组没有任何含义
初始化时,将dis数组的每个值置为0,甚至任意数都是可以的,只要保证每个位置的值一样
由于现在要判断图中是否存在负环,若从一个点出发找负环,在非连通图中可能无法实现,所以不能用单源点判断负环。所以初始化时,将所有的点入队,表示每个点都是源点,此时就算是非连通图也能找到负环
dis[x] + w[i] < dis[y]:什么时候会执行该操作?当dis所有值都相同时,只有w[i]为负数(存在负权边)时,该操作才会执行。此时维护cnt数组,cnt[y] = cnt[x] + 1表示遍历了某条存在负权边的路径中的一条边,当cnt数组中的某个值大于等于n,即cnt[i] >= n时,根据抽屉原理,该图中存在负环

所以,求解负环时,SPFA不再是单源求解过程,而是多源求解过程。求解的问题也不再是最短路,而是负环的判断
此时,dis数组的含义发生变化,同时引入cnt数组,存储遍历某条存在负权边的路径的边的次数

非连通图中的负环,边数不可能大于等于n吧。那么根据cnt[i] >= n判断图中是否存在负环有问题吗?cnt数组存储在某条存在负权边的路径中遍历的次数,当一个负环的边数小于n,那么我们将一直遍历这个负环,直到cnt[i] >= n停止
最坏的情况时,所有的点共同构成了负环,负环中点的数量与边的数量都等于n,此时我们将遍历图中所有点一次,接着cnt[i] == n,遍历结束,说明图中存在负环

模板:

int h[N], e[M], ne[M], w[M], idx = 1;
int q[N], hh, tt = -1;
int dis[N], cnt[n];
bool st[N];

void add(int x, int y, int d)
{
	e[idx] = y, ne[idx] == h[x], w[idx] = d, h[x] = idx ++ ;
}

bool spfa()
{
	for (int i = 1; i <= n; ++ i )
		q[++ tt] = i, st[i] = true;

	while (tt >= hh)
	{
		int x = q[hh ++];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i])
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y])
			{
				dis[y] = dis[x] + w[i];
				cnt[y] = cnt[x] + 1;
				if (cnt[y] >= n) return true;
				if (!st[y]) st[y] = true, q[++ tt] = y;
			}
		}
	}
	return false;
}

多源汇最短路

Floyd

任意起点与终点的最短路问题
Floyd的时间复杂度为: O( n 3 n^3 n3)

d[k][i][j]:从i号点出发,只经过1~k号这些中间点,到达j号点的最短距离
状态更新:d[k][i][j] = d[k - 1][i][k] + d[k - 1][k][j],观察表达式,发现第一个维度可以压缩,即d[i][j] = d[i][k] + d[k][j]

过程很简单,三重循环进行动态规划
模板:

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]);
}

注意d数组要初始化:初始化的值与邻接矩阵一样,由于要求解的问题是多源汇最短路,所以要初始化源点到源点的距离为0


最短路练习题

849. Dijkstra求最短路 I

849. Dijkstra求最短路 I - AcWing题库
对图中节点进行编号,题目要求1号点到n号点的最短距离
以1号点作为源点,用朴素Dijkstra更新dis数组,获取单源最短路

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510, M = 1e5 + 10;
int dis[N], g[N][N];
bool vt[N];
int n, m;

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    
    for (int i = 0; i < n; ++ i )
    {
        int x = -1;
        for (int j = 1; j <= n; ++ j)
            if (!vt[j] && (x == -1 || dis[j] < dis[x])) 
                x = j;

        vt[x] = true; // 从不属于s的点中选择距离源点最近的点
        for (int y = 1; y <= n; ++ y) // 用新加入的点更新其他点
            dis[y] = min(dis[y], dis[x] + g[x][y]);
    }
}

int main()
{
    memset(g, 0x3f, sizeof(g));
    
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        g[x][y] = min(g[x][y], d);
    }
    
    dijkstra();
    
    if (dis[n] == 0x3f3f3f3f) printf("-1\n");
    else printf("%d\n", dis[n]);
    
    return 0;
}

850. Dijkstra求最短路 II

850. Dijkstra求最短路 II - AcWing题库
image.png

题目给定的图为稀疏图,使用邻接表存储
和第一题一样,要求1号点到n号点的最短距离,以1号点为源点,用Dijkstra求得单源最短路

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII>> q;
const int N = 2e5 + 10;
int h[N], e[N], ne[N], w[N], idx = 1;
int dis[N];
bool vt[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    q.push({ 0, 1 }), dis[1] = 0;
    
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        
        int x = t.second, d = t.first;
        if (vt[x]) continue;
        vt[x] = true;
        
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!vt[y] && d + w[i] < dis[y])
            {
                dis[y] = d + w[i];
                q.push({ dis[y], y });
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    dijkstra();
    if (dis[n] == 0x3f3f3f3f) printf("-1\n");
    else printf("%d\n", dis[n]);
    
    return 0;
}

debug:优先队列需要建小堆,默认是建立大堆,这个真的没有注意到
queue<PII, vector<PII>, greater<PII>>:传greater建立小堆


853. 有边数限制的最短路

853. 有边数限制的最短路 - AcWing题库
image.png

求解具有负权边的最短路问题一般使用SPFA,因为其时间复杂度较低。但有些最短路问题只能用Bellman-Ford求解,因为这类题有边数的限制
限制的边数就是外循环的次数,内循环还是要对所有边进行松弛操作
由于图中可能存在负权边,当源点无法递达某一个点时,该点的dis距离可能不是最大值,而是小于最大值的较大值
假设源点无法到达目标点y点,但x点能到达y点,此时源点也无法递达x点,即dis[x] = +∞。而连接x与y的边权为负数,那么目标点y的dis距离将被更新:dis[y] = dis[x] + w,其中的dis[x]为正无穷,w为负数,那么dis[y]将被更新成一个小于正无穷的较大数
所以判断源点是否能递达其他点时,不能判断dis[i] == 0x3f3f3f3f,而应该判断dis[i] > 0x3f3f3f3f / 2

以下代码存在问题,无法AC

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510, M = 10010;
int dis[N], bup[N];
int n, m, k;

struct Edge
{
    int x, y ,w;
}edges[M];

int Bellman_Ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto e = edges[j];
            dis[e.y] = min(dis[e.y], bup[e.x] + e.w);
        }
    }
    
    if (dis[n] > 0x3f3f3f3f / 2) return -1;
    return dis[n];
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    int x, y, w;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &w);
        edges[i] = { x, y, w };
    }
    
    int t = Bellman_Ford();
    if (t == -1) puts("impossible");
    else printf("%d\n", t);
    
    return 0;
}

debug:某些最短路可能是负值,当最短路为-1时,Bellman_Ford()返回的t也是-1,此时-1不能作为区分是否存在最短路的值
所以应该直接在main函数中判断dis[n]0x3f3f3f3f的关系

以下是ac代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510, M = 10010;

struct Edge
{
    int x, y, w;
}edges[M];

int dis[N], bup[N];
int n, m, k;

void bellman_ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto t = edges[j];
            dis[t.y] = min(dis[t.y], bup[t.x] + t.w);
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    int x, y, d;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        edges[i] = { x, y, d };
    }
    
    bellman_ford();
    if (dis[n] > 0x3f3f3f3f / 2) puts("impossible");
    else printf("%d\n", dis[n]);
    
    return 0;
}

851. spfa求最短路

851. spfa求最短路 - AcWing题库
image.png

debug:以下spfa是错误的,错在松弛操作的判断条件,当满足dis[x] + w[i] < dis[y]时,就要更新dis[y] = dis[x] + w[i]。然后再判断y是否在队列中,不能将y是否在队列作为松弛操作的判断条件

void spfa()
{
    memset(dis, 0x3f, sizeof(dis));
    q[++ tt] = 1, dis[1] = 0, st[1] = true;
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!st[y] && dis[x] + w[i] < dis[y])
                st[y] = true, dis[y] = dis[x] + w[i], q[++ tt] = y;
        }
    }
}

以下是ac代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1e5 + 10;

int h[N], e[N], ne[N], w[N], idx = 1;
int q[N], hh, tt = -1;
int dis[N];
bool st[N]; // 某个点是否在队列中
int n, m;


void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa()
{
	memset(dis, 0x3f, sizeof(dis));
	q[++ tt] = 1, st[1] = true, dis[1] = 0; // 假设1号点为源点
	while (tt >= hh)
	{
		int x = q[hh ++ ];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i] )
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y]) 
			{
				dis[y] = dis[x] + w[i];
				if (!st[y]) st[y] = true, q[++ tt ] = y;
			}
		}
	}
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    spfa();
    
    if (dis[n] == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", dis[n]);
    return 0;
}

852. spfa判断负环

852. spfa判断负环 - AcWing题库
image.png

#include <iostream>
#include <cstring>
using namespace std;

const int N = 2010, M = 10010;
int h[N], e[M], ne[M], w[M], idx = 1;
int q[M], hh, tt = -1;
int dis[N], cnt[N];
bool st[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool spfa()
{
    for (int i = 1; i <= n; ++ i )
        q[++ tt] = i, st[i] = true;
    
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[x] + w[i] < dis[y])
            {
                dis[y] = dis[x] + w[i];
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n) return true;
                if (!st[y]) q[++ tt] = y, st[y] = true;
            }
        }
    }
    return false; 
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    if (spfa()) puts("Yes");
    else puts("No");
    
    return 0;
}

debug:用原生数组模拟队列时,队列的长度要设置为M,之前设置为int q[N],导致了TLE,排查了很久的逻辑错误,结果是这里错了。看来这种边界问题需要提前想好啊,懒得想就用STL的,但是时间差距嘛,看下图
image.png


854. Floyd求最短路

854. Floyd求最短路 - AcWing题库
image.png

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 210, M = 20010;
int n, m, k;
int d[N][N];

void flody()
{
    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()
{
    memset(d, 0x3f, sizeof(d));
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++ i ) d[i][i] = 0;
    
    int x, y, w;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &w);
        d[x][y] = min(d[x][y], w);
    }
    
    flody();
    
    while (k -- )
    {
        scanf("%d%d", &x, &y);
        int t = d[x][y];
        if (t > 0x3f3f3f3f / 2) puts("impossible");
        else printf("%d\n", t);
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统远程挂载Mac OS系统目录方法

打开mac文件共享功能 开启共享服务 进入系统偏好设置中的共享选项。勾中文件共享&#xff08;如下图&#xff09;&#xff0c;之后右边的文件共享的绿灯会点亮&#xff0c;并显示“文件共享&#xff1a;打开”。 添加共享目录 点击在文件共享界面&#xff08;如下图&#x…

【狂神】MySQL - 数据库级别的外键

1. 外键 FOREIGN KEY (了解) 测试数据 &#xff1a; 学生表 CREATE TABLE IF NOT EXISTS student (id INT(4) NOT NULL AUTO_INCREMENT COMMENT 学号,name VARCHAR(30) NOT NULL DEFAULT 匿名 COMMENT 姓名,pwd VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT 密码,sex VARC…

【数据结构与算法】 完成用十字链表存储的稀疏矩阵的加法运算

题目&#xff1a; Qestion: 完成用十字链表存储的稀疏矩阵的加法运算。 主要思路&#xff1a; 获取两个稀疏矩阵总有多少个非零元素&#xff0c;记作cnt。当cnt 不为零时一直循环&#xff0c;每循环一次i&#xff0c;也就是行循环&#xff0c;每循环一次就转移至下一行。先从…

Git常用指令总结

1、git init&#xff1a;初始化一个Git仓库&#xff1b; 2、git clone&#xff1a;从远程仓库克隆代码到本地&#xff1b; 直接使用网址 git clone <url>or 用a代替网址 git remote add a <url>git clone a3、git add&#xff1a;添加文件到暂存区&#xff1b; 文件…

K8S数据管理

K8S数据管理 1 数据管理1.1 数据持久化1.1.1 存储方案1.1.2 EmptyDir实践1.1.3 hostPath实践1.1.4 NFS实践 1.2 持久化进阶1.2.1 数据对象1.2.2 PV&PVC实践1.2.3 SC解析1.2.4 SC实践 1.3 配置管理1.3.1 配置基础1.3.2 CM1.3.3 CM案例1.3.4 Secret1.3.5 Secret案例 1.4 状态…

36. QT中使用QFtp实现文件传输1 -- 本地文件或文件夹上传到远程服务器

1. 说明 在使用QT进行嵌入式开发或者是使用到TCP控制传输时,有时程序的正常运行会用到某一个文件或者整个文件夹,此时就需要软件方面将需要的文件或者文件夹传输到远程服务器上。在QT中主要有两种方式可以实现这个功能,一个是QT4中使用QFtp这个类来实现,这个类提供了很丰富…

每天一点Python——day48

#第四十八天 #什么是元组为什么元组没有增删改操作和生成式&#xff1f; 元组&#xff1a;Python内置的数据结构之一&#xff0c;是一个不可变序列 不可变序列&#xff1a;没有增删改操作【例如字符串&#xff0c;元组】 可变序列&#xff1a;可以执行增删改操作&#xff0c;操…

记录好项目D21

记录好项目 你好呀&#xff0c;这里是我专门记录一下从某些地方收集起来的项目&#xff0c;对项目修改&#xff0c;进行添砖加瓦&#xff0c;变成自己的闪亮项目。修修补补也可以成为毕设哦 本次的项目是个基于Springbootvue的景区旅游系统 一、系统介绍 本项目分为管理员与…

534 · 打劫房屋 II

链接&#xff1a;LintCode 炼码 - ChatGPT&#xff01;更高效的学习体验&#xff01; 题解&#xff1a;九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 处理循环数组问题&#xff1a;分类&#xff0c;重复&#xff0c;取反…

信息安全概述笔记

保密性、完整性、可用性是传统的信息安全的原则和目标&#xff0c;目前随着信息安全问题的日益严峻&#xff0c;信息安全的原则和目标衍生为诸如可控性、不可否认性等其他的原则和目标。 保密性&#xff08;Confidentiality&#xff09;:确保信息只能由那些被授权使用的人获取…

Web服务器群集:四层代理与七层代理

目录 一、理论 1.OSI七层模型 2.四层代理 3.七层代理 4.四层代理与七层代理区别 5.负载均衡器 6.常见的代理组件 7.应用场景 二、总结 一、理论 1.OSI七层模型 &#xff08;1&#xff09;概念 标准的七层网络分层是OSI七层模型&#xff0c;TCP/IP五层模型和TCP/IP四…

Android Matrix的理解

文章目录 前言一.基础1.1 Matrix1.2 使用Matrix的准备知识 二.preXXX和postXXX2.1 右乘和左乘2.2 验证规律 三.坐标原点结束 前言 Android绘制中最重要的要算Matrix类了&#xff0c;同时也是不太好理解的。以前也用过&#xff0c;但是掌握的也不是太好&#xff0c;刚好有时间好…

GO语言包相关总结 -引用(本地和远程),自定义,安装,使用

本篇文章总结以下go语言包相关的知识。 目录 一.导入包 &#xff08;1&#xff09;常规导入 &#xff08;2&#xff09;别名导入 &#xff08;3&#xff09;特殊导入 二.自定义包 三.安装自定义包 四.调用自定义包调用 五.获取远程包 六.go中的保留函数 七.实战 - G…

Python自动化测试实战篇(12),一文学完,Pytest 常用11种第三方插件

这些是之前的文章&#xff0c;里面有一些基础的知识点在前面由于前面已经有写过&#xff0c;所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇&#xff08;1&#xff09; Python自动化测试实战篇&#xff08;2&#xff09; Python自动化测试实战篇&#xff…

解决python-opencv:(-215:Assertion failed) _img.empty() in function ‘cv::imwrite‘在将视频分成帧图片,写入时出现的问题

最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&#xff0c;了解了视频分帧…

rk3399 调试ap6354

电路如下: wifi&#xff1a; 按照rk3399 sdk默认配置&#xff0c;修改相应的引脚 sdio_pwrseq: sdio-pwrseq { compatible "mmc-pwrseq-simple"; clocks <&rk808 1>; clock-names "ext_clock"; pinctrl-nam…

filter功能演示-鉴权、声明缓存

文章目录 Filter定义工作原理Filter所处环节 Demo示例总结 Filter定义 在Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;中&#xff0c;过滤器&#xff08;Filter&#xff09;是一种强大的组件&#xff0c;用于在Web应用程序中拦截和处理传入的请求和响应…

银河麒麟服务器V10 SP1 .Net6.0 开机自启动

开机自动启动&#xff0c;折腾了一小天&#xff0c;设置/etc/init.d/ 、update-rc.d&#xff0c;可能刚开始用&#xff0c;经验不多吧&#xff0c;尝试多种方式我的服务怎么都启动不起来&#xff0c;根据之前nginx和redis的自动启动经验&#xff0c;使用systemd管理服务&#x…

Unity基础 物理系统 刚体组件下的移动.碰撞.触发检测

当在Unity中创建游戏或应用程序时&#xff0c;重力系统是一个非常重要的组成部分。它可以模拟物体受到地球引力的影响&#xff0c;并产生逼真的物理效果。在Unity中&#xff0c;我们可以使用刚体组件和重力向量来控制重力系统。 首先&#xff0c;在Unity中创建一个物体&#xf…

数据表示与数据编码

数据表示与数据编码 数据表示 bit:二进制位 例如:480Mbps(Mb/s) 小写字母b代表bitbyte:字节 1byte8bit 使用大写字母B表示 byte最初从IBM360中开始表示word:字长 在32bit计算机中一个字长为32位&#xff0c;在64bit计算机中一个字长为64位 最早的微处理器字长为4位 章节学习内…