1.深度优先搜素
2.宽度优先搜索
3.树与图的存储
4.树与图的DFS
5.树与图的BFS
6.拓扑排序
从使用数据结构来看
DFS stack
BFS queue
从使用空间来看
DFS O(H); “不具有最短性”
BFS O(2^h) “最短路”(第一次搜到的距离为最短距离)
1.深度优先搜索
(1)回溯 : 注意恢复现场
(2)剪枝 : 最优型剪枝 可行性剪枝
一个DFS一定对应一个搜索树
最重要的想清楚顺序
例1.数字排列
#include<bits/stdc++.h>
using namespace std;
const int N = 20;
int path[N], vis[N];
int n;
void dfs(int x)
{
if (x > n) {
for (int i = 1; i <= n; i++)
cout << path[i] << ' ';//输出路径
cout << endl;
return;
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
vis[i] = 1;//标记用过
path[x] = i;//加入路径
dfs(x + 1);//枚举下一个位置
vis[i] = 0;//恢复现场
}
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
例2:n皇后问题
顺序1:枚举每一行的皇后放在什么位置
/ /
/ 正对角线上的元素坐标和相同 dg[x + y];
\\ 反对角线上的元素坐标差相同 由于y - x可能是负数所以加上n
dg[y - x + n];
#include<bits/stdc++.h>
using namespace std;
int n;
const int N = 20;
bool pal[N],pg[N],upg[N];
char g[10][10];
void dfs(int x) {
if (x > n)
{
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
cout << g[i][j];
cout << endl;
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++) {
if (!pal[i] && !pg[x + i] && !upg[n - i + x])
{
pal[i] = pg[x + i] = upg[n - i + x] = true;
g[x][i] = 'Q';
dfs(x + 1);
pal[i] = pg[x + i] = upg[n - i + x] = false;
g[x][i] = '.';
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = '.';
dfs(1);
system("PAUSE");
return 0;
}
第二种
这种方式是更无脑的搜索
#include<bits/stdc++.h>
using namespace std;
int n;
const int N = 20;
char g[N][N];
// 依次表示 行 列 正对角线 负对角线
bool row[N], col[N], pg[N], upg[N];
void dfs(int x, int y, int s) { //枚举行列和已经放皇后的个数
if (y > n) {//注意! 这个是本题的重点
x++; //当y枚举到超过边界时 要让他指向下一行的第一个元素
y = 1;
}
if (s >= n) {
if (s == n) { //如果放皇后数目正好为n则输出方案
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << g[i][j];
}
cout << endl;
}
cout << endl;
}
return;
}
if (x > n) //如果x超过边界则return
return;
dfs(x, y + 1, s);//当前位置不放皇后
//判断 行 列 正对角线 负对角线
if (!row[x] && !col[y] && !pg[x + y] && !upg[n - x + y])
{
row[x] = col[y] = pg[x + y] = upg[n - x + y] = true;
g[x][y] = 'Q'; //标记
dfs(x, y + 1, s + 1);//这个位置放皇后 枚举下一个位置
row[x] = col[y] = pg[x + y] = upg[n - x + y] = false;
g[x][y] = '.';//恢复现场
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = '.';
dfs(1, 1, 0);
return 0;
}
2.BFS
第一次搜到的一定是最小的(图中边的权重相同)
只有边的权重都为1时求最短路才可以使用BFS
DP是一种特殊的最短路 内部没有环的最短路
基本步骤
初始-> queue
while queue 不空
{
t->队头
拓展t
}
BFS在拓展时也要用vis 进行判断
因为只有BFS第一次搜到的点才是最短距离
例1
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int a[N][N];
int vis[N][N];
int n, m;
int ans = 0x3f3f3f3f;
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
int k = 1;
bool check(int x, int y)
{
return x >= 1 && x <= n && y >= 1 && y <= m;
}
struct node {
int x;
int y;
int sum;
}dia[100];
void bfs(int x,int y,int sum) {
queue<node> q;
node dian;
dian.x = x; dian.y = y; dian.sum = sum;
q.push(dian);
while (!q.empty())
{
int xx = q.front().x;
int yy = q.front().y;
int ss = q.front().sum;
q.pop();
if (xx == n && yy == m) {
cout << ss << endl;
return;
}
for (int i = 0; i < 4; i++) {
int nowx = xx + dx[i];
int nowy = yy + dy[i];
if (check(nowx, nowy) && !vis[nowx][nowy] && a[nowx][nowy] == 0)
{
node dian;
dian.x = nowx; dian.y = nowy; dian.sum = ss + 1;
vis[nowx][nowy] = 1;
q.push(dian);
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
bfs(1,1,0);
system("PAUSE");
return 0;
}
例2
#include<bits/stdc++.h>
using namespace std;
#include<unordered_map>
int bfs(string start)
{
queue<string > q;
vector<pair<string, int> > v;
q.push(start);
v
}
int main()
{
string start;
cin >> start;
cout << bfs(start) << endl;
system("PAUSE");
return 0;
}
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
int bfs(string state) {
queue<string> q;
unordered_map<string, int> d;
q.push(state);
d[state] = 0;
int dx[4] = { -1,0,1,0 };
int dy[4] = { 0,1,0,-1 };
string end = "12345678x";
while (q.size())
{
string t = q.front();
q.pop();
if (t == end)return d[t];
int distance = d[t];
int k = t.find('x');
int x = k / 3, y = k % 3;
for (int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3) {
swap(t[a * 3 + b], t[k]);
if (!d.count(t))
{
d[t] = distance + 1;
q.push(t);
}
swap(t[a * 3 + b], t[k]);
}
}
}
return -1;
}
int main()
{
char s[2];
string state;
for (int i = 0; i < 9; i++)
{
cin >> s;
state+= *s;
}
cout << bfs(state) << endl;
system("PAUSE");
return 0;
}
3.图论
树和图是怎么存储的
树和图有两种存储方式
树是一种特殊的图,树是无环连通图
图分为有向图和无向图
一、只需要考虑有向图如何存储:
1.邻接矩阵(用的比较少) 比较浪费空间
二维数组 g[a][b] 表示a到b这一条边 g[a][b]的值就是a到b的权重
2.邻接表
为图中的每一个点都开一个单链表 存储这个点可以到达的点,单链表中的顺序无关紧要
插入一条新的边从a到b,找到a所在的链表,从a的头把b插入进去
模板:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010, M = N * 2;//图是双向的,a可以到b,b也可以到a,n个点的图最多有2n条边
int n, m;
//h数组表示每个点的链表的头节点,e表示节点的值,ne表示节点的next指针,idx表示当前存储到的位置
int h[N], e[M], ne[M], idx;
void add(int a, int b) {//新建立一条边,将b这个节点插入到a链表头节点的后面
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
//h[a]=idx++
}
int main()
{
memset(h, -1, sizeof(h));//将头节点都指向-1(都指向空节点)
return 0;
}
二、树和图的遍历
1.深度优先遍历
2.宽度优先遍历
都是每个点只遍历一次
模板
#include<bits/stdc++.h>
using namespace std;
const int N = 100010, M = N * 2;//图是双向的,a可以到b,b也可以到a,n个点的图最多有2n条边
int n, m;
//h数组表示每个点的链表的头节点,e表示节点的值,ne表示节点的next指针,idx表示当前存储到第几条边
int h[N], e[M], ne[M], idx;
bool vis[N];
void add(int a, int b) {//新建立一条边,将b这个节点插入到a链表头节点的后面
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
//h[a]=idx++
}
//res 当前连通块的最大值
void dfs(int u) {
vis[u] = true;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!vis[j])dfs(j);//一条路走到黑
}
}
int main()
{
memset(h, -1, sizeof(h));//将头节点都指向-1(都指向空节点)
dfs(1);
return 0;
}
最短路径 只遍历一次
图的BFS的应用
求拓扑序:
1、概念
拓扑序列 针对于有向图->对于每条边都是起点在终点的前面
如果存在环 则一定不存在拓扑序
一个有向无环图(拓扑图)一定存在拓扑序列
入度:一个节点有几条边进来
出度: 一个节点有几条边出去
2、 拓扑序列的实现
所有入度为0的点都可以作为一个起点
所有入度为0的点入队
while(q不空){
t=队头
枚举t的所有出边 t->j
删掉t->j (因为t已经在j的前面了)
d[j]-- d数组表示为j的入度
if(d[j]==0)
queue <-- j
如果j的入度减为0了则j也可以作为一个起点入队
}
图的层次遍历·
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = N * 2;
int n, m;
int h[N], e[M], ne[M], vis[N], idx;
long long ans = 0x3f3f3f3f;
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void bfs(int num) {
queue<pair<int,long long> > q;
q.push(make_pair(num,0));
vis[num] = 1;
while (!q.empty())
{
pair<int,int> now = q.front();
q.pop();
for (int i = h[now.first]; i != -1; i = ne[i]) {
int j = e[i];
int foot = now.second + 1;
if (j == n)
{
ans = foot;
return;
}
if (vis[j])
continue;
q.push(make_pair(j,foot));
}
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
add(a, b);
}
bfs(1);
if (ans = 0x3f3f3f3f)
cout << -1 << endl;
else cout << ans << endl;
system("PAUSE");
return 0;
}
有向图的拓扑排序
/*图的bfs的应用
求拓扑序:
1、概念
拓扑序列 针对于有向图->对于每条边都是起点在终点的前面
如果存在环 则一定不存在拓扑序
一个有向无环图(拓扑图)一定存在拓扑序列
入度:一个节点有几条边进来
出度 : 一个节点有几条边出去
2、 拓扑序列的实现
所有入度为0的点都可以作为一个起点
所有入度为0的点入队
while (q不空) {
t = 队头
枚举t的所有出边 t->j
删掉t->j(因为t已经在j的前面了)
d[j]--d数组表示为j的入度
if (d[j] == 0)
queue < --j
如果j的入度减为0了则j也可以作为一个起点入队
}
一个有向无环图的拓扑序不是唯一的*/
/*
数组模拟队列实现BFS
hh=0,tt=-1 hh代表队头 tt 代表队尾
入队操作 q[++tt]=a;
获取队头操作 t=q[hh++];
判断队列是否为空 while(hh<=tt)
出队操作q[++tt]=j;
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int M = 2 * N;
int n, m;
int h[N], e[M], ne[M], idx, d[N], vis[N];
int q[N];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int topsort()
{
int hh = 0, tt = -1;
for (int i = 1; i <= n; i++)
{
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];
if (--d[j] == 0)
q[++tt] = j;
}
}
return tt == n - 1;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof(h));
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
add(a, b);
d[b]++;
}
if (!topsort())
{
cout << -1 << endl;
}
else {
for (int i = 0; i < n; i++)
cout << q[i] << ' ';
cout << endl;
}
return 0;
}