图论基础
深度优先遍历
深度优先遍历(DFS), 指从图中一个未遍历的节点u开始, 沿着一条路一直走到底, 然后从这条路尽头回退到上一个节点, 再从另一条路开始走到底. 不断递归重复此过程, 直到所有节点都遍历完成.
深度优先遍历经典问题Ⅰ 排列数字
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10;
int n;
int path[N];
bool st[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++)cout<<path[i]<<' ';
cout<<'\n';
return;
}
for(int i=1;i<=n;i++)
if(!st[i])
{
path[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false; //恢复现场 + 回溯
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
深度优先遍历经典问题Ⅱ n-皇后问题
//第一种搜索顺序(按行搜索)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20;
int n;
char g[N][N];
bool col[N],dg[N],udg[N]; //col表示列,dg表示“/”对角线,udg表示“\”对角线
void dfs(int u) //u按行搜索
{
if(u==n)
{
for(int i=0;i<n;i++)cout<<g[i]<<'\n';
cout<<'\n';
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;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//第二种搜索顺序(按每一个格子搜索)
#include<iostream>
#include<algorithm>
using namespace std;
const int N=20;
int n;
char g[N][N];
bool row[N],col[N],dg[N],udg[N];
void dfs(int x,int y,int s) //s是皇后个数
{
if(y==n)y=0,x++;
if(x==n)
{
if(s==n)
{
for(int i=0;i<n;i++)cout<<g[i]<<'\n';
cout<<'\n';
}
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);
row[x]=col[y]=dg[x+y]=udg[x-y+n]=false; //恢复现场+回溯
g[x][y]='.';
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0,0,0);
return 0;
}
宽度优先遍历
宽度优先遍历(BFS), 又称广度优先遍历, 指从图的一个未遍历的节点出发, 先遍历这个节点的相邻节点, 再依次遍历每个相邻节点的相邻节点.
宽度优先遍历经典题目Ⅰ 走迷宫
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=110;
int n,m;
int g[N][N],d[N][N];
//g[][]用于存储迷宫地图,d[][]用于记录每个点是否被搜索过,存储每一个点到起点的距离
int bfs()
{
queue<pair<int,int>>q;
memset(d,-1,sizeof d);
d[0][0]=0;
q.push({0,0});
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; //偏移量
while(q.size())
{
auto t=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int x=t.first+dx[i],y=t.second+dy[i];
if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]=-1)
{
d[x][y]=d[t.first][t.second]+1;
q.push({x,y});
}
}
}
return d[n-1][m-1];
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>g[i][j];
cout<<bfs()<<endl;
return 0;
}
宽度优先遍历经典问题Ⅱ 八数码
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>
using namespace std;
int bfs(string start)
{
string end="12345678x";
queue<string>q;
unordered_map<string,int>d;
q.push(start);
d[start]=0;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
while(q.size())
{
auto t=q.front();
q.pop();
int distance=d[t];
if(t==end)return distance;
//状态转移
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[k],t[a*3+b]);
if(d.count(t)==0)
{
d[t]=distance+1;
q.push(t);
}
swap(t[k],t[a*3+b]);
}
}
}
return -1;
}
树与图的存储
数是一种特殊的图(无环连通图)
图分为有向图、无向图
无向图是特殊的有向图, 对无向图中的边ab, 存储两条有向边 a \(\to\) b , b \(\to\) a
\[有向图的存储 \begin{cases} 邻接矩阵(不能处理重边) \\[4ex] 邻接表 \end{cases} \]
int h[N],e[N],ne[N],idx;
//添加一条从a到b的边
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
树与图的深度优先遍历
void dfs(int u)
{
st[u]=true; //表示点u已经被遍历过
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j])dfs(j);
}
}
树与图的宽度优先遍历
queue<int>q;
st[1]=true; //表示1号点已经被遍历过
q.push(1);
while(q.size())
{
int t=q.front();
q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j])
{
st[j]=true;
q.push(j);
}
}
}
拓扑排序
拓扑排序是图的宽度优先搜索的一个应用
拓扑图即有向无环图
//拓扑排序模板
bool topsort()
{
int hh=0,tt=-1;
//d[i]存储点i的入度
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;
}