8.1深度优先搜索DFS
1.概念:
类似于图中走迷宫,只要遇到岔路口就继续深入下去,一直到遍历完所有的岔路口,不碰到死胡同不回头
2.使用递归的方式来实现深度优先搜索:
3.看一个具体的例子:
//物品编号index
//物品总重量sumW,总价值sumC
//如果不放入index号物品,则sumW不变,总价值sumC也不变
//如果index+1,则前往DFS(index+1,sumW+w[index],sumC+c[index]);
//一直到index增长到n,说明把n件物品遍历完了,此时sumw和sumc就是所选物品总质量和总价值,如果sumW不超过V,并且sumC大于最大价值记录就更新max
/*
解题思路:
1.DFS就只有两种思路:
1.第一种是到达死胡同,也就是index一直遍历到n了
2.第二种是遇到岔路口,就只有两种选择:一种是进行下一件产品并不改变总价值和总重量,另一中是进行到下一个index物品,并且改变总重量和总价值
2.main:
1.输入n件物品和对应的价值
2.初始化DFS全为0
3.最后输出最大值即可
*/
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=30;
int v,maxvalue=0,n;
int w[maxn],c[maxn];
void DFS(int index,int sumW, int sumC)
{
if(index==n)
{
if(sumW<=v && sumC>=maxvalue)
{
maxvalue=sumC;
}
return ; //注意return的位置
}
DFS(index+1,sumW,sumC);
DFS(index+1,sumW+w[index],sumC+c[index]);
}
int main()
{
cin >> n >> v;
for(int i=0; i<n; i++)
cin >> w[i];
for(int i=0; i<n; i++)
cin >> c[i];
DFS(0,0,0);
cout << maxvalue;
}
8.2广度优先搜索(BFS)
概念:
下面这张图是对上面的总结:
BFS的模板
void BFS(int s)
{
queue<int > q;
q.push(s);
while(!q.empty())
{
取出队首元素top
访问队首元素top
将队首元素出队
将top的下一层节点中未曾入队的结点全部入队,并设置为已入队
}
}
例子1:
下张图是对上面例题的解释
#include<iostream>
#include<queue>
using namespace std;
const int maxn=100;
struct node
{
int x,y;
}Node;
int matrix[maxn][maxn];
bool inq[maxn][maxn]={false}; //inq中表示为true的点其实都表示的是某一块中的点,并且所有能入队列中的点也都是这一块中的点
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
int n,m;
bool judge(int x, int y)
{
if(x<0 || x>=n || y<0 || y>=m)
return false;
if(matrix[x][y]==0 || inq[x][y]==true) //matrix中有俩个值,如果是0肯定不用管,如果inq为true表示这个点已经计算过
return false;
return true;
}
//设置结点结构node,结构元素为(x,y)
//矩阵大小为n*m,matrix矩阵为输入的01矩阵,inq记录位置x,y是否已经入过队列
//两个增量数组X,Y分别表示上下左右:(x+1,y)(x-1,y)(x,y-1)(x,y+1)
//判断(x,y)是否需要访问,如果超出边界则返回false,如果元素值为0或者已经如果入过队列也返回false,以上都不满足则返回true
/*
BFS访问其所在的块,函数参数是x,y
设置一个结构队列:Q,将元素的位置x,y放到队列中
按照BFS模板:
队列Q不为空
取出队首元素
队首元素出队
循环4次:访问该结点的上下左右4个位置,并用judge进行判断;如果满足条件重新设置一个结点的位置,把合适的点放到队列中,并且将对应的位置的值标记为true
BFS的作用:将某一个点的上下左右位置的点如果是1则全部标记为true,同时
*/
/*
main函数:
1.输入n,m
2.读入矩阵
3.设置块数
4.双层for循环读入枚举每一个位置,如果元素值为1并且数字为false,则块数加1并且BFS访问整个块
*/
void BFS(int x, int y) //BFS只能访问某一块的点
{
queue<node> Q;
Node.x=x;
Node.y=y;
Q.push(Node);
inq[x][y]=true;
while(!Q.empty())
{
node q=Q.front();
Q.pop();
for(int i=0; i<4; i++) //上下左右4个点
{
int newX=q.x+X[i]; //注意这一定是队列中第1个结点的x和y
int newY=q.y+Y[i];
if(judge(newX,newY))
{
Node.x=newX; //这个Node还是之前的
Node.y=newY;
Q.push(Node);
inq[newX][newY]=true;
}
}
}
}
int main()
{
cin >> n >> m;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
cin >> matrix[i][j];
int ans=0;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
{
if(matrix[i][j]==1 && inq[i][j]==false) //使用两个数组matrix和inq,其中matrix表示数字01,inq表示true或者false
{
ans++;
BFS(i,j); //遍历了每一块的点
}
}
cout << ans;
return 0;
}
列子2:
#include<iostream>
#include<queue>
using namespace std;
const int maxn=100;
struct node
{
int x,y,step;
}S,T,Node;
char matrix[maxn][maxn];
bool inq[maxn][maxn]={false}; //注意inq表示元素是否进入过队列,而非是否已经访问过,区别在于:如果设置为是否已经访问过时,就会重复计算点
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
int n,m;
bool judge(int x, int y)
{
if(x<0 || x>=n || y<0 || y>=m)
return false;
if(matrix[x][y]=='*') //如果遇到*,表示遇到墙壁则返回false
return false;
if(inq[x][y]==true)
return false;
return true;
}
int BFS() //不要参数
{
queue<node> Q;
Q.push(S);
while(!Q.empty())
{
node top=Q.front();
Q.pop(); //每次从队列中拿到一个值,肯定要弹出一个值
if(top.x==T.x && top.y==T.y) //如果队首元素对应的值是终点的所以直接返回相关的长度
{
return top.step;
}
for(int i=0; i<4; i++)
{
int newX=top.x+X[i];
int newY=top.y+Y[i];
if(judge(newX,newY))
{
Node.x=newX;
Node.y=newY;
Node.step=top.step+1;
Q.push(Node);
inq[newX][newY]=true;
}
}
}
return -1;
}
int main()
{
cin >> n >> m;
for(int i=0; i<n; i++)
{
getchar();
for(int j=0; j<m; j++)
{
cin >> matrix[i][j]; //注意将字符输入进去
}
matrix[i][m+1]='\0';
}
cin >> S.x >> S.y >> T.x >> T.y;
S.step=0;
printf("%d",BFS());
return 0;
}
将结构数组放到对列中的注意事项:
#include<iostream>
#include<queue>
using namespace std;
/*
解题思路:
1.用STL中对列时,将结构数组放到队列中,改变队列中的值不会改变数组结构的值
2.同样改变结构数组中的值不会改变队列中的值
3.队列相当于创建了该结构数组的一个副本
*/
struct node
{
int x;
}a[10];
int main()
{
queue<node> q;
for(int i=1; i<=3; i++)
{
a[i].x=i;
q.push(a[i]); //将结构数组放到队列中
}
q.front().x=1000; //改变队列中的值不会改变结构数组中的值
cout << a[1].x << " " << a[2].x << " " << a[3].x << endl;
a[1].x=200; //同样改变结构数组中的值不会改变队列中的值
cout << q.front().x;
return 0;
}
所以说如果想通过队列改变值的话,最好在队列中存放元素的下标
#include<iostream>
#include<queue>
using namespace std;
/*
解题思路:
1.用STL中对列时,将结构数组放到队列中,改变队列中的值不会改变数组结构的值
2.同样改变结构数组中的值不会改变队列中的值
3.队列相当于创建了该结构数组的一个副本
*/
struct node
{
int x;
}a[10];
int main()
{
queue<int> q;
for(int i=1; i<=3; i++)
{
a[i].x=i;
q.push(i); //将结构数组放到队列中
}
a[q.front()].x=100; //这个相当于直接改变结构数组的值
cout << a[1].x << " " << a[2].x << " " << a[3].x;
return 0;
}