【高效复习】算法合集(二)——DFS和BFS

DFS

DFS核心:

构造递归,F(0)、F(1)是递归边界,递归式就是岔路口。枚举的思想+递归的表达方式:
提示:类似全排列模板,给定一个序列,枚举这个序列的所有子序列(可以不连续)

void DFS(int index, int sumw,int sumc)
{
    if(index==n){
        if(sumw<=v && sumc>maxvalue)
            maxvalue=sumc;
    }
    DFS(index+1,sumw,sumc);
    DFS(index+1,sumw+w[index],sumc+c[index]);
}

简单剪枝:

void DFS(int index, int sumw,int sumc)
{
    if(index==n){
        return;
    }
    DFS(index+1,sumw,sumc);
    if(sumw+w[index]<=v){  // 当数量不超过时记录并递归下去
        if(sumc+c[index]>ans)
            ans=sumc+c[index];
   	DFS(index+1,sumw+w[index],sumc+c[index]);
    }
}

图的DFS

相较于一般DFS多出了vis判断,依旧是枚举+递归;
伪代码:

DFS(u){
    vis[u]=true;
    for(从u出发能到达的所有顶点v)
        if vis[v]==false
            DFS(V);
}
DFSTrave(G)
{
    for(G的所有顶点u)
        if vis[u]==false
            DFS(u);
}

1)邻接矩阵

const int Maxv=1000;//最大顶点
const int Inf=100000000;
int n,G[Maxv][Maxv];
bool vis[Maxv]={false};
 
void DFS(int u,int depth){
    vis[u]=true;
    //从u出发能到达的所有顶点v
    for(int v=0;v<n;v++)
        if(vis[v]==false && G[u][v]!=Inf){
            DFS(v,depth+1);
        }
}
void DFSTrave()
{
    for(int u=0;u<n;u++){
    //G的所有顶点u
        if(vis[u]==false)
            DFS(u,1);
    }
}

2)邻接表

const int Maxv=1000;//最大顶点
const int Inf=100000000;
//区别一:用vector定义邻接表
vector<int> adj[Maxv];
int n;
bool vis[Maxv]={false};
 
void DFS(int u,int depth){
    vis[u]=true;
    //从u出发能到达的所有顶点v
    for(int v=0;v<n;v++){
	//区别二:提取出结点u->i
        int v=adj[u][i]; 
        if(vis[v]==false){
            DFS(v,depth+1);
        }
    }
}
void DFSTrave()
{
    for(int u=0;u<n;u++){
    //G的所有顶点u
        if(vis[u]==false)
            DFS(u,1);
    }
}

BFS:

场景:适合走迷宫
基本模板:

void BFS(int s)
{
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        取出队首元素top;
        访问队首元素top;
        将队首元素出队;
        将top的下层结点中未曾入队的结点全部入队,并设置为已入队;
    }
}

提示:对于迷宫类,会用到增量数组
int x[]={1,-1,0,0};
int y[]={0,0,1,-1};

详细版本:
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int maxn=100;
struct node{
    int x,y;
}nod;

int n,m;
int matrix[maxn][maxn];
//定义的是入队情况,BFS只允许入队一次,而不是是否遍历/visit,因为后者可能出现多次入队
//(比如,A、B都连接C,A访问时C入队,B访问时有可能再一次让C入队。)
bool inq[maxn][maxn]={false}; 

int Y[]={1,-1,0,0};
int X[]={0,0,1,-1};
//边界检查
bool judge(int x,int y)
{
    if(x>=n ||x<0 || y>=m || y<0) return false;
    if(matrix[x][y]==0 || inq[x][y]==true ) return false;
    return true;
}

void BFS(int x,int y){
    queue<node> q;
    nod.x=x,nod.y=y;
    q.push(nod);
    inq[x][y]=true;
    while(!q.empty())
    {
        node top=q.front();
        q.pop();
 //将top的下层结点中未曾入队的结点全部入队,并设置为已入队;
        for(int i=0;i<4;i++)
        {
            int newx=top.x+X[i];
            int newy=top.y+Y[i];
//剪枝
            if(judge(newx,newy)){
                nod.x=newx,nod.y=newy;
                q.push(nod);
                inq[newx][newy]=true;
            }
        }
    }
}

细节强调:STL的queue时,元素入队的push操作只是制造了该元素的一个副本入队,因此在入队后对原元素的修改不会影响队列中的副本,而对队列中副本的修改也不会改变原元素。因而会引起bug(一般由结构体产生)。
即入队时q.push(i);比q.push(a[i]);好前者可以直接通过a[q.front()]对结构体或者数组进行修改。

强化详见PAT A1091 Acute Stroke (30 分)
https://pintia.cn/problemsets/994805342720868352/problems/994805375457411072

图的BFS

模板:
邻接矩阵版:

int n,G[maxv][maxv];
bool inq[maxv]={false};
void BFS(int u)
{
    queue<int> q;
    q.push(u);
    inq[u]=true;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int v=0;v<n;v++)
            if(inq[v]==false && G[u][v]!=inf)
            {
                q.push(v);
                inq[v]=true;
            }
    }
}
BFStrave(G)
{
    for(int u=0;u<n;u++){
        if(inq[u]==false)
        {
            BFS(u);
        }
    }
}

邻接表:

int n;
//区别一:定义不一样
vector<int> adj[maxv];
bool inq[maxv]={false};
void BFS(int u)
{
    queue<int> q;
    q.push(u);
    inq[u]=true;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        //区别二:取点方式有差异
        for(int i=0;i<adj[u].size();i++){
            int v=adj[u][i];
            if(inq[v]==false && G[u][v]!=inf)
            {
                q.push(v);
                inq[v]=true;
            }
        }
    }
}
BFStrave(G)
{
    for(int u=0;u<n;u++){
        if(inq[u]==false)
        {
            BFS(u);
        }
    }
}

图算法存储方式提示

领接表:

vector adj[n];
如,添加一条从1号顶点到3号顶点的有向边:adj[1].push_back(3);
同时存放边的终点和边权:
struct node{
int v; //边的终点编号
int w; //边权
}
vector adj[n];
更快的是构造结构体node的构造函数
struct node
{
int v,w;
Node(int _v,int _w):v(_v),w(_w) {}
//构造函数(就是new对象时,能够同时进行一些操作,比如赋值什么的.
//如果你对构造方法进行重载的话,就可以根据不同的参数来构造不同初始值的对象.)
}
好处是不用定义临时变量,adj[1].push_back(Node(3,4));

猜你喜欢

转载自blog.csdn.net/lyly1995/article/details/87867340