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
#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;
}
基础算法 搜索与图论 二、三未看