DFS基础——迷宫

迷宫

配套视频讲解

关于dfs和bfs的区别讲解。

对于上图,假设我们要找从1到5的最短路,那么我们用dfs去找,并且按照编号从大到小的顺序去找,首先找到的路径如下,

从节点1出发,我们发现节点2可以走,于是我们就走向了节点2,然后又发现节点2可以走向节点4,于是走向了节点4,然后从节点4走向了节点5,我们只是找到了一条从节点1到节点5的路径,但是我们并不能确定是否是最短路径,所以我们还需要继续去找。我们回退到节点4,与节点4相连的节点3和节点5都已经被遍历过了,所以我们回退到节点2,与节点2相连的节点1和节点4都已经被遍历过了,所以我们回退到节点1,此时节点3也与节点1相连,于是我们走向节点3,后面的路径如下图所示,

我们通过节点3走向了节点5。然后返回到节点3,与节点3相连的节点1和节点5都已经被遍历过了,所以我们回退到节点1,此时与节点1相连的节点2和节点3都已经被遍历过了,dfs退出。

我们找到了两条路径,通过对比这两天路径的长度,我们可以找到最短路径,可以看出,我们穷尽了所有从节点1到达节点5的路径。

接下来看一看bfs是怎么找的。

我们是用队列实现的bfs过程,一开始队列里面只有起始点,即[1]。从队列里面拿出一个节点,即节点1,然后去走此时节点1能够到达的所有节点。如上图所示,bfs会同时向多个方向拓展,节点1与节点2,节点3相连,那么第一步它会从节点1走向节点,也会从节点1走向节点3。这时分别将节点2和节点3加入队列,并且此时,节点1已经出队,即[2,3]。然后去走所有与节点2相连且没有被遍历过的节点,再去走所有与节点3相连且没有被遍历过的节点,如下图所示,

第二步从节点3走到节点5,也从节点2走向了节点4,但是此时我发现已经走到终点了,并且长度为2,即便在后续遍历中我可以走到终点,但是现在所到达的所有点长度已经是2了,那么后续继续走长度一定比2大,所以我可以确定,此时的路径一定是最短路。

dfs题目分析

这一道题我们可以用dfs也可以用bfs,但是对于迷宫最短路类题目,最好的方法是bfs。通过这道题如果采用dfs来做,需要用到dfs里的回溯和剪枝。

首先分析题意,我们要找从起点到终点的最短路,并且这个最短路的行走路线是字典序最小,其实字典序最小好操作,我们只需要在遍历上下左右四个方向的时候按照字典序从小到大的顺序遍历就行了。即

static char[] direct = {'D','L','R','U'};
static int[] nexty = {0,-1,1,0};
static int[] nextx = {1,0,0,-1};

接下来考虑如何找最短路,对于dfs而言,要找最短路,我必须把当前可走的所有路都遍历结束后,才能确定哪一条路是最短路,对于某一个位置,我当前向左走,标记左边的节点为已遍历过,并且把这个’L’存入,走到终点后我再回退,那么再次回到这个位置时,我会选择其它可以走的方向,那么我们要把左边的节点已遍历过的标记清空,表示没有遍历过,并且把在这里存入的’L’取出。即

for (int i = 0; i < 4; i++) {int xx = x + nextx[i];int yy = y + nexty[i];if(xx >= 0 && xx < n && yy >= 0 && yy < m &&visit[xx][yy] == 0 && map[xx][yy] == '0') {visit[xx][yy] = 1;path[s] = direct[i];//更新路径......dfs(xx, yy, s+1);visit[xx][yy] = 0;//回溯 dfs返回的时候,往往需要对之前做的标记进行重置	}}

解释一下上述代码,for (int i = 0; i < 4; i++)表示四个方向遍历,它遍历的顺序由nextx和nexty数组决定,而我们在最开始就给他规定了遍历的顺序是按字典序从小到大遍历的。然后if语句一是判断下一个位置的坐标是否越界,二是判断下一个位置是否之前被遍历过,三是判断下一个位置是否可以走,都没问题的话我就去走这个位置,然后标记这个位置被走过,并且存入此时走的方向,visit[xx][yy] = 1;path[s] = direct[i];,然后就是进入dfs,那么这里dfs三个参数分别表示下一个位置的x坐标,y坐标,以及走到下一个位置走了几步。dfs结束后,我要给visit标记复原,即visit[xx][yy] = 0;

上述是正常dfs以及回溯的过程,接下来我们要加入剪枝。什么情况下我可以确定这条路一定不会成为答案?也就是这条路的长度超过了我们此时记录的最短路的长度,即

if(s >= step) {return;
}

s表示我走到当前节点的步数,step表示此时记录的最短路的长度,我还没有走到终点步数就比最短路长,那么它必然不会成为最短路,所以后面就不需要遍历了,直接返回。

还有第二种剪枝,这个不太好想,也是比较最短路的长度,我们比较的是到达当前位置的长度以及在之前我走到该位置的最短长度。比如有一个点A,我走到这里耗费了s步,但是我之前记录我走到这里耗费了s1步,而s1<s,那么说明我之前走到这里再向后走的路径一定比我现在走到这里再向后走的路径短,那么此时我就没有必要遍历了。即

if(s > dp[x][y]) {return;
}

s表示我走到当前节点(x,y)的步数, d p [ x ] [ y ] dp[x][y] dp[x][y]表示我之前走到该节点的最短路。那么在dfs的过程中我们要更新dp数组,

for (int i = 0; i < 4; i++) {int xx = x + nextx[i];int yy = y + nexty[i];if(xx >= 0 && xx < n && yy >= 0 && yy < m &&visit[xx][yy] == 0 && map[xx][yy] == '0') {visit[xx][yy] = 1;dp[xx][yy] = Math.min(dp[xx][yy], s+1);//更新dppath[s] = direct[i];//更新路径dfs(xx, yy, s+1);visit[xx][yy] = 0;//回溯 dfs返回的时候,往往需要对之前做的标记进行重置//path[s] = '';}}

在回溯的时候我们只回溯了visit数组,为什么呢?dp数组不需要回溯,因为它记录的就是一个全局的值,即在我所有到达(x,y)点的路径中最短路径的长度。而path数组虽然需要回溯,但是我们在下一个遍历的时候,下一个的值会直接覆盖之前的值,所以不需要特意给他回溯。那么你也可以理解有回溯,即注释的那个地方//path[s] = '';,这里写和不写效果是一样的。

然后我们看当走到终点时,如何处理,

//判断是否走到终点
if(x == n-1 && y == m-1) {if(s < step) {//当前步数小于之前的最优值,对结果进行一下记录step = s;String string = "";for (int i = 0; i < s; i++) {//System.out.print(" "+path[i] + " ");string += path[i];}//set.add(string);result = string;}//System.out.println();
}

判断一下此时走到终点的路径长度是否小于我之前记录的长度,如果小于,我要更新最短路径长度,以及这条路径每一步走的方向。

最后这道题,给我们的图是一个字符串,我们可以把它转化成二维字符数组,转化细节在shuju函数里。

dfs题目代码

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.Set;public class Main{/** 在步数最少的前提下,请找出字典序最小的一个作为答案* 如何保证找到的第一个路径一定是字典序最小的* 遍历的时候,按照字典序从小到大的顺序去遍历* D<L<R<U* DDRURRDDDR* DRRURRDDDR* 如何记录我们的路径* bfs * node{* x,y,path//走到当前节点的路径* }* path*/static int n = 30;static int m = 50;static char[][]  map = new char[n][m];static char[] direct = {'D','L','R','U'};static int[] nexty = {0,-1,1,0};static int[] nextx = {1,0,0,-1};static int[][] visit = new int[n][m];//dfsstatic char[] path = new char[n*m+1];static int step = n*m+1;static String result = "";static Set<String> set = new HashSet<String>();static int[][] dp = new int[n][m];
public static void main(String[] args) {Scanner sc = new Scanner(System.in);shuju();for(int i=0;i<n;i++){Arrays.fill(dp[i], n*m+1);}sc.close();visit[0][0] = 1;dfs(0,0,0);System.out.println(result);
}
private static void shuju() {// TODO Auto-generated method stubString string = "01010101001011001001010110010110100100001000101010"+"00001000100000101010010000100000001001100110100101"+"01111011010010001000001101001011100011000000010000"+"01000000001010100011010000101000001010101011001011"+"00011111000000101000010010100010100000101100000000"+"11001000110101000010101100011010011010101011110111"+"00011011010101001001001010000001000101001110000000"+"10100000101000100110101010111110011000010000111010"+"00111000001010100001100010000001000101001100001001"+"11000110100001110010001001010101010101010001101000"+"00010000100100000101001010101110100010101010000101"+"11100100101001001000010000010101010100100100010100"+"00000010000000101011001111010001100000101010100011"+"10101010011100001000011000010110011110110100001000"+"10101010100001101010100101000010100000111011101001"+"10000000101100010000101100101101001011100000000100"    //D -> L -> R -> U+"10101001000000010100100001000100000100011110101001"+"00101001010101101001010100011010101101110000110101"+"11001010000100001100000010100101000001000111000010"+"00001000110000110101101000000100101001001000011101"+"10100101000101000000001110110010110101101010100001"+"00101000010000110101010000100010001001000100010101"+"10100001000110010001000010101001010101011111010010"+"00000100101000000110010100101001000001000000000010"+"11010000001001110111001001000011101001011011101000"+"00000110100010001000100000001000011101000000110011"+"10101000101000100010001111100010101001010000001000"+"10000010100101001010110000000100101010001011101000"+"00111100001000010000000110111000000001000000001011"+"10000001100111010111010001000110111010101101111000";//将上面的一大串字符转为数组中存储,且数组为int型,仅存0 和 1两个值 int[][] moze= new int[30][50];for (int i = 0 ; i < 30 ; i++) {for(int j = 0 ; j < 50 ; j++) {map[i][j] = string.charAt(i*50+j); //}}
}
private static void dfs(int x, int y, int s) {//s当前已经走的步数  走的路径的方向path[i] 表示第i步走的方向/* 剪枝* 1.s走到x,y所需要的步数,step当前记录的走完迷宫所需要的最短的步数,s>step return* 2.dp[x][y] 当前走到x,y点所需要的最短距离,s>dp[x][y] return* * * dfs{* 1.剪枝* 2.回溯* 3.递归* * }*///剪枝操作if(s >= step) {return;}if(s > dp[x][y]) {return;}//判断是否走到终点if(x == n-1 && y == m-1) {//System.out.print("---" + x + " " + y + " ");if(s < step) {//当前步数小于之前的最优值,对结果进行一下记录step = s;String string = "";for (int i = 0; i < s; i++) {//System.out.print(" "+path[i] + " ");string += path[i];}//set.add(string);result = string;}//System.out.println();}for (int i = 0; i < 4; i++) {int xx = x + nextx[i];int yy = y + nexty[i];if(xx >= 0 && xx < n && yy >= 0 && yy < m &&visit[xx][yy] == 0 && map[xx][yy] == '0') {visit[xx][yy] = 1;dp[xx][yy] = Math.min(dp[xx][yy], s+1);//更新dppath[s] = direct[i];//更新路径dfs(xx, yy, s+1);visit[xx][yy] = 0;//回溯 dfs返回的时候,往往需要对之前做的标记进行重置}}
}
}

bfs题目分析

迷宫最短路问题最简单的做法就是使用bfs去做,bfs与dfs的区别是bfs第一次走到终点的路径一定是最短路,他不用把所有可行路径都遍历一次。接下来大致讲一下bfs的过程。

在bfs的过程我是同时多个方向扩展路径,所以每一个点,当前我走到这里需要的步数以及这条路上的方向我都要记录,所以我要自己写一个节点类,里面存了节点坐标和走到该点的一个路径,这个路径既表示了这条路上每一步走的方向也表明了路的长度。x和y表示当前点的坐标,pathString表示走到当前点的路径。

static class node{int x;int y;String pathString;public node(int x, int y, String pathString) {super();this.x = x;this.y = y;this.pathString = pathString;}}

首先把起点加入队列,并标记起点已经被访问过,

LinkedList<node> queue = new LinkedList<>();//申请一个队列
queue.add(new node(0, 0, ""));//把起点放入队列
visit[0][0] =1;
String shunxv ="";//记录最短路径

然后依次从队列里面取出点,直到队列为空。取出点后我们先判断该点是否为终点节点,如果是说明我们找到了一条最短路,后续不需要遍历了,直接退出。否则我要向四个方向去遍历。通过if语句判断下一个位置的坐标是否越界,下一个位置是否可走,下一个位置是否已经被遍历过,都满足的话我就可以走向下一个位置,把它对应的信息添加到队列里面,然后标记该位置已经被走过。

while(!queue.isEmpty()){node  t = queue.poll();int x1 = t.x;int y1 = t.y;String str1 = t.pathString;if(x1==n-1&&y1==m-1){//判断是否走到终点shunxv = str1;break;}for(int i=0;i<4;i++){//向四个方向去遍历int x2= x1+nextx[i];int y2= y1+nexty[i];if(x2>=0&&x2<=n-1&&y2>=0&&y2<=m-1&&map[x2][y2]=='0'&&visit[x2][y2]!=1){queue.add(new node(x2, y2, str1+direct[i]));visit[x2][y2]=1;}}
}

bfs题目代码

import java.util.LinkedList;
import java.util.Scanner;
public class Main{/** 在步数最少的前提下,请找出字典序最小的一个作为答案* 如何保证找到的第一个路径一定是字典序最小的* 遍历的时候,按照字典序从小到大的顺序去遍历* D<L<R<U* DDRURRDDDR* DRRURRDDDR* 如何记录我们的路径* bfs * node{* x,y,path//走到当前节点的路径* }* path*/static class node{int x;int y;String pathString;public node(int x, int y, String pathString) {super();this.x = x;this.y = y;this.pathString = pathString;}}static int n = 30;static int m = 50;static char[][]  map = new char[n][m];static char[] direct = {'D','L','R','U'};static int[] nexty = {0,-1,1,0};static int[] nextx = {1,0,0,-1};static int[][] visit = new int[n][m];static String res;
public static void main(String[] args) {Scanner sc = new Scanner(System.in);shuju();sc.close();bfs();
}
private static void shuju() {// TODO Auto-generated method stubString string = "01010101001011001001010110010110100100001000101010"+"00001000100000101010010000100000001001100110100101"+"01111011010010001000001101001011100011000000010000"+"01000000001010100011010000101000001010101011001011"+"00011111000000101000010010100010100000101100000000"+"11001000110101000010101100011010011010101011110111"+"00011011010101001001001010000001000101001110000000"+"10100000101000100110101010111110011000010000111010"+"00111000001010100001100010000001000101001100001001"+"11000110100001110010001001010101010101010001101000"+"00010000100100000101001010101110100010101010000101"+"11100100101001001000010000010101010100100100010100"+"00000010000000101011001111010001100000101010100011"+"10101010011100001000011000010110011110110100001000"+"10101010100001101010100101000010100000111011101001"+"10000000101100010000101100101101001011100000000100"    //D -> L -> R -> U+"10101001000000010100100001000100000100011110101001"+"00101001010101101001010100011010101101110000110101"+"11001010000100001100000010100101000001000111000010"+"00001000110000110101101000000100101001001000011101"+"10100101000101000000001110110010110101101010100001"+"00101000010000110101010000100010001001000100010101"+"10100001000110010001000010101001010101011111010010"+"00000100101000000110010100101001000001000000000010"+"11010000001001110111001001000011101001011011101000"+"00000110100010001000100000001000011101000000110011"+"10101000101000100010001111100010101001010000001000"+"10000010100101001010110000000100101010001011101000"+"00111100001000010000000110111000000001000000001011"+"10000001100111010111010001000110111010101101111000";//将上面的一大串字符转为数组中存储,且数组为int型,仅存0 和 1两个值 int[][] moze= new int[30][50];for (int i = 0 ; i < 30 ; i++) {for(int j = 0 ; j < 50 ; j++) {map[i][j] = string.charAt(i*50+j); //}}
}
private static void bfs() {// TODO Auto-generated method stubLinkedList<node> queue = new LinkedList<>();//申请一个队列queue.add(new node(0, 0, ""));//把起点放入队列visit[0][0] =1;String shunxv ="";//记录最短路径while(!queue.isEmpty()){node  t = queue.poll();int x1 = t.x;int y1 = t.y;String str1 = t.pathString;if(x1==n-1&&y1==m-1){//判断是否走到终点shunxv = str1;break;}for(int i=0;i<4;i++){//向四个方向去遍历int x2= x1+nextx[i];int y2= y1+nexty[i];if(x2>=0&&x2<=n-1&&y2>=0&&y2<=m-1&&map[x2][y2]=='0'&&visit[x2][y2]!=1){queue.add(new node(x2, y2, str1+direct[i]));visit[x2][y2]=1;}}}System.out.println(shunxv);
}
}

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

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

相关文章

在Linux上运行JMeter(非界面)

参考&#xff1a; 查看文件类型&#xff1a;https://www.linuxprobe.com/files-tehre-fangfa.html 华为云平台 配置&#xff1a;jdk环境、jmeter环境 jmeter配置&#xff08;在/etc/profile文件中&#xff09;&#xff1a; export JMETER_HOME/path/to/jmeter/installati…

嵌入式学习41-数据结构2

今天学习了链表的增删改查 &#xff08;暂定&#xff01;&#xff01;后续再补内容&#xff09; 高内聚 &#xff1a;一个函数只实现一个功能 …

Docker 镜像仓库

目录 1、搭建私有 registry 服务端创建镜像仓库 客户端推送镜像 镜像导入导出 2、Nginx 代理 registry 仓库 SSL 证书 & https 协议 SSL证书 https协议 SSL 的验证流程 客户端安装 Nginx 使用 openssl 生成CA根证书和根证书key 创建 Nginx 服务证书 配置启动 N…

Airgorah:一款功能强大的WiFi安全审计工具

关于Airgorah Airgorah是一款功能强大的WiFi安全审计工具&#xff0c;该工具可以轻松发现和识别连接到无线接入点的客户端&#xff0c;并对特定的客户端执行身份验证攻击测试&#xff0c;捕捉WPA握手包&#xff0c;并尝试破解接入点的密码。在该工具的帮助下&#xff0c;广大研…

在Ubuntu上使用Script命令捕获命令与其输出

在Ubuntu上使用Script命令捕获命令与其输出 起初&#xff0c;是为了记录软件的安装过程&#xff0c;就在想有没有简单高效的记录方法&#xff0c;之后就找到了script命令。 使用 script命令&#xff0c;可以很容易地记录下你在终端里所有的操作与输出&#xff0c;非常适合用来…

是时候来唠一唠synchronized关键字了,Java多线程的必问考点!

写在开头 在之前的博文中&#xff0c;我们介绍了volatile关键字&#xff0c;Java中的锁以及锁的分类&#xff0c;今天我们花5分钟时间&#xff0c;一起学习一下另一个关键字&#xff1a;synchronized。 synchronized是什么&#xff1f; 首先synchronized是Java中的一个关键字…

Tensorflow 2.0 常见函数用法(一)

文章目录 0. 基础用法1. tf.cast2. tf.keras.layers.Dense3. tf.variable_scope4. tf.squeeze5. tf.math.multiply 0. 基础用法 Tensorflow 的用法不定期更新遇到的一些用法&#xff0c;之前已经包含了基础用法参考这里 &#xff0c;具体包含如下图的方法&#xff1a; 本文介…

dbscan算法实现鸢尾花聚类(python实现)

DBscan算法原理 : dbscan算法-CSDN博客 法一(调库) : 直接调库 : import numpy as np import matplotlib.pyplot as plt from sklearn import datasets from sklearn.cluster import DBSCAN from sklearn.decomposition import PCA from sklearn.discriminant_analysis …

leetcode 225.用队列实现栈 JAVA

题目 思路 1.一种是用双端队列&#xff08;Deque&#xff09;&#xff0c;直接就可以调用很多现成的方法&#xff0c;非常方便。 2.另一种是用普通的队列&#xff08;Queue&#xff09;,要实现栈的先入后出&#xff0c;可以将最后一个元素的前面所有元素出队&#xff0c;然后…

【Flask】Flask项目结构初识

1.前提准备 Python版本 # python 3.8.0 # 查看Python版本 python --version 安装第三方 Flask pip install flask # 如果安装失败&#xff0c;可以使用 -i&#xff0c;指定使用国内镜像源 # 清华镜像源&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/ 检查 Flask 是…

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述…

Qt如何直接处理系统事件(比如鼠标事件),而不是post事件

#include <QtGui/5.15.2/QtGui/qpa/qwindowsysteminterface.h> // 方便调试事件 QWindowSystemInterface::setSynchronousWindowSystemEvents(true); 直接再 qWindowsWndProc函数中处理 通常情况: 事件被放到一个队列中

基于springboot+vue+Mysql的垃圾分类网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

JavaScript高级(十)----JavaScript中的类【重述原型链】!

类 在JavaScript其实本来没有类的概念&#xff0c;哪怕是ES5以后的class&#xff0c;严格意义上来说也只是构造函数的语法糖&#xff0c;之所以喜欢称之为类&#xff0c;因为JavaScript也可以面向对象开发。 类的声明 class Person {}function Person1() {}// 上面两种写法本…

Milvus 向量数据库介绍及使用

一、Milvus 介绍及安装 Milvus 于 2019 年创建&#xff0c;其目标只有一个&#xff1a;存储、索引和管理由深度神经网络和其他机器学习 (ML) 模型生成的大量嵌入向量。它具备高可用、高性能、易拓展的特点&#xff0c;用于海量向量数据的实时召回。 作为专门为处理输入向量查…

Linux:权限的概念与理解

目录 1. Linux权限的概念 2. Linux权限管理 01.文件访问者的分类 02.文件类型和访问权限 03.文件权限值的表示方法 04. 文件访问权限的相关设置方法 3. 使用 sudo分配权限 4. 目录的权限 ---------- 权限 用户角色(具体的人) 文件权限属性 ---------- 1. Linux权限的…

代码随想录--排序算法

912.排序数组 快速排序 思路&#xff1a; 1. 设置一个pivot2. 将小于nums[pivot]的值 放在左边3. 将 大于nums[pivot]的值 放在 右边4. 递归调用注意&#xff1a;必须先比较nums[high] 与pivot 代码&#xff1a; class Solution {int partition(vector<int>&nu…

无人机采集图像的相关知识

1.飞行任务规划 一般使用飞行任务规划软件进行飞行任务的设计&#xff0c;软件可以自动计算相机覆盖和图像重叠情况。比如ArduPilot (ArduPilot - Versatile, Trusted, Open) 和UgCS (http://www.ugcs.com)是两个飞行任务规划软件&#xff0c;可以适用大多数无人机系统。 2.图…

QT作业。。

1.使用手动连接&#xff0c;将登录框中的取消按钮使用t4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数将登录按钮使用t5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断u界面上输入的账号是否为"admin"&#xff0c;密码是否为&q…

【计算机视觉】Gaussian Splatting源码解读补充(一)

本文旨在补充gwpscut创作的博文学习笔记之——3D Gaussian Splatting源码解读。 Gaussian Splatting Github地址&#xff1a;https://github.com/graphdeco-inria/gaussian-splatting 论文地址&#xff1a;https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gauss…