acwing搜索与图论

dfs 与 bfs
宽搜与深搜没有固定的模板,要理解其思想再具体到题目里,用邻接表存储/dfs bfs 遍历

在这里插入图片描述

dfs
842.排列数字 模板题
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <cstring>

using namespace std;

const int N = 10;

int n;
int path[N];    // 保存路径
bool st[N];      // st[i] 判断数i是否被用过

void dfs(int u)     // u表示当前枚举到n的哪一位
{
    if(u == n)  // 当前走到n最后一位--输出方案
    {
        for(int i = 0; i < n; i ++ ) cout << path[i] << ' ';
        cout << endl;   // 每一个方案后输出一个空行
        return;
    }      
    
    for(int i = 1; i <= n; i ++ )
        if(!st[i])      // 如果当前这个数没有被用过就将其放到位置u上并标记这个数已经被用过
        {
            path[u] = i;    // path[u] 每次都会被覆盖掉,恢复现场可省去 path[u] = 0;
            st[i] = true;
            //	在这一层递归修改状态
            dfs(u + 1);
            // 进入下一层递归之前恢复状态---递归函数结束之后一定要恢复现场
            st[i] = false;      // 恢复现场
        }
}

int main()
{
    cin >> n;
    
    dfs(0);     // 从第0位开始枚举
    
    return 0;
}


843.n皇后问题 模板题
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

/*dfs 
O(n * n!)
采用全排列思想,依次枚举每一行放的皇后位置,判断 列 对角线 反对角线 状态,
放置完一个皇后标记对应位置占用,递归到向下一层继续枚举
*/
#include <iostream>
#include <cstring>

using namespace std;

const int N = 10;

int n;
char g[N][N];       // 用字符串存放方案
bool col[N], dg[N], udg[N];     // 判断列 对角线 反对角线 占用状态

void dfs(int u)     // 枚举到第几个皇后
{
    if(u == n)
    {
        for(int i = 0; i < n; i ++ ) puts(g[i]);
        puts("");
        return;
    }
    
    for(int i = 0; i < n; i ++ )
    {
        if(!col[i] && !dg[u + i] && !udg[n - u + i])     // 判定这一列 对角线 反对角线都没有放过
        {
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
            g[u][i] = '.';
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++ )
        for(int j = 0; j < n; j ++ )
            g[i][j] = '.';
    
    dfs(0);
    return 0;
}
/*
O(n ^ 2)
较原始的一种做法
在棋盘上依次枚举每一个格子并判断能否放置皇后
*/
#include <iostream>
#include <cstring>

using namespace std;

const int N = 10;

int n;
char g[N][N];
bool row[N], col[N], dg[N], udg[N];


void dfs(int x,int y, int s)    // 行 列  已经摆放皇后的个数
{
    if(y == n) y = 0,x ++;   // 当每一行y枚举到最后一格(y == n),让(x, y)移动到下一行第一格
    
    if(x == n)
    {
        if(s == n)  // 枚举到第n行且已经摆了n个皇后就输出棋盘;可能存在枚举到第n行但只摆了几个皇后,此时不能输出
        {
            for(int i = 0; i < n; i ++ ) puts(g[i]);
            puts("");
        }
        return;
    }
    
    // 不放皇后
    dfs(x, y +1, s);
    
    // 放皇后
    if(!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
    {
        g[x][y] = 'Q';
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
        
        dfs(x, y + 1, s + 1);   // 递归到下一行
        
        g[x][y] = '.';      // 回溯 恢复现场
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
    }
    
}

int main()
{
    cin >> n;
    
    for(int i = 0; i < n; i ++ )
        for(int j = 0; j < n; j ++ )
            g[i][j] = '.';
    
    // dfs(0);
    dfs(0, 0, 0);   // 关键在于搜索的顺序
            
    return 0;
}

bfs
只有所有边都是1,才可以用bfs求最短路

844.走迷宫 模板题 ???? 01:09:44
在这里插入图片描述

有向图的邻接表dfs

846.树的重心 模板题
在这里插入图片描述在这里插入图片描述
树和图的遍历
树和图的存储
在这里插入图片描述

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

using namespace std;

const int N = 1e5 + 10; //数据范围是10的5次方
const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边

int h[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
int e[M]; //存储元素
int ne[M]; //存储列表的next值
int idx; //单链表指针
int n; //题目所给的输入,n个节点
int ans = N; //表示重心的所有的子树中,最大的子树的结点数目

bool st[N]; //记录节点是否被访问过,访问过则标记为true

//a所对应的单链表中插入b  a作为根 
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

// dfs 框架
/*
void dfs(int u){
    st[u]=true; // 标记一下,记录为已经被搜索过了,下面进行搜索过程
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]) {
            dfs(j);
        }
    }
}
*/

//返回以u为根的子树中节点的个数,包括u节点
int dfs(int u) {
    int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数
    st[u] = true; //标记访问过u节点
    int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点

    //访问u的每个子节点
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        //因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过
        if (!st[j]) {
            int s = dfs(j);  // u节点的单棵子树节点数 如图中的size值
            res = max(res, s); // 记录最大联通子图的节点数
            sum += s; //以j为根的树 的节点数
        }
    }

    //n-sum 如图中的n-size值,不包括根节点4;
    res = max(res, n - sum); // 选择u节点为重心,最大的 连通子图节点数
    ans = min(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数
    return sum;
}

int main() {
    memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点
    cin >> n; //表示树的结点数

    // 题目接下来会输入,n-1行数据,
    // 树中是不存在环的,对于有n个节点的树,必定是n-1条边
    for (int i = 0; i < n - 1; i ++ ) 
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a); //无向图
    }

    dfs(1); //可以任意选定一个节点开始 u<=n

    cout << ans << endl;

    return 0;
}

bfs

847.图中点的层次
在这里插入图片描述

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

using namespace std;

const int N = 1e5 + 10;

int h[N], e[N], ne[N], idx;
int d[N], q[N];

int n, m;   // n 个点 m 条边的有向图

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int bfs()
{
    int hh = 0, tt = 0;   // 队头 队尾
    q[0] = 1;
    
    memset(d, -1, sizeof d);    // 初始化距离  -1 表示没有被遍历过
    d[1] = 0;  
    
    while(hh <= tt)     // 队列不为空
    {
        int t = q[hh ++ ];   // 队头元素出队  h 指针向后移一位(代表队列出一个元素)
        
        // 遍历循环所有与t相距为1的点    
        for(int i = h[t]; i != -1; i = ne[i])   // ne[i] 上的点与i相距为1
        {
            int j = e[i];
            if(d[j] == -1)  // 表示当前节点未被扩展过
            {
                d[j] = d[t] + 1;    // 路径长度都是1,所以在上一步基础上 +1
                q[ ++ tt] = j;  // j入队
            }
        }
    }
    return d[n];    // 返回节点1到节点n的距离
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);    //  初始化来链表--头节点置 -1
    
    // 读入m条边(从 a -> b 的长度为 1 的边)
    for(int i = 0; i < m; i ++ )
    {
        int a, b;
        cin >> a >> b;
        add(a,b);
    }
    
    cout << bfs() << endl;
    
    return 0;
}

拓扑序列

848.有向无环图的拓扑序列
在这里插入图片描述
bfs模板在这里插入图片描述在这里插入图片描述

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

const int N = 100010;

int n, m;
int e[N], h[N], ne[N], idx;

int q[N], d[N];     // q[] 队列存放入度为0的点, d[i] 存放下标为i的点的入度是多少

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool topsort()
{
    int hh = 0, tt = -1;
    
    for(int i = 1; i <= n; i ++ )   // 从前往后遍历n个点
        if(!d[i])   // 找到入度为0的点
            q[ ++ tt] = i;  // 入队
            
    while(hh <= tt)     // 队列不为空
    {
        int t = q[hh ++ ];   // 取出队头元素,队头指针向后移动一位--出队
        
        for(int i = h[t]; i != -1; i = ne[i])   // 拓展队头元素--以队头为头节点的链表
        {
            int j = e[i];
            d[j] -- ;       // 找到出边并 入度 -- 
            if(d[j] == 0)  
                q[ ++ tt] = j;   // 把入度为0的点入队
        }
    }
    return tt == n - 1;     // 判断所有点是否都已入队--队尾指针指向 (n - 1) ,共入队 n 个点
        // 当所有点全部入队,说明这是一个有向无环图,存在拓扑序列;否则图中存在环--不存在拓扑序
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    
    for(int i = 0; i < m; i ++ )    // 输入m条边
    {
        int a, b;
        cin >> a >> b;
        add(a, b);      // 插入一条 a -> b 的边
        d[b] ++ ;       // 更新入度,b节点多一条入边--节点b入度+1
    }
    
    if(topsort())   // 判断是否存在拓扑序,存在则输出,不存在则输出 -1
    {
        for(int i = 0; i < n; i ++ ) printf("%d ",q[i]);
        puts("");
        
    }
    else puts("-1");
    
    return 0;
}

基础算法 搜索与图论 二、三未看

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值