DFS 重点在回溯恢复现场
全排列 按每个空位DFS
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int n;
int path[N];
int used[N];
void DFS(int u){
if(u==n){
for(int i=0;i<n;i++){
cout<<path[i]<<" ";
}
cout<<endl;
return;
}
for(int i=1;i<=n;i++){
if(!used[i]){
path[u]=i;
used[i]=1;
DFS(u+1);
used[i]=0; 回溯恢复
}
}
}
int main(){
cin>>n;
DFS(0);
}
N皇后问题 按行DFS 注意两个对角线的处理
对角线处理:
#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n;
char g[N][N];
int col[N],dg[N],udg[N];
void DFS(int u){
if(u==n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<g[i][j];
}
cout<<endl;
}
cout<<endl;
}
else{
for(int i=0;i<n;i++){
if(!col[i]&&!dg[u+i]&&!udg[n+i-u]){ 剪枝
col[i]=dg[u+i]=udg[n+i-u]=1;
g[u][i]='Q';
DFS(u+1);
回溯
col[i]=dg[u+i]=udg[n+i-u]=0;
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);
}
BFS
走迷宫 核心在于:steps[N][N]维护步数 prev[N][N]维护路径
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int mp[N][N];
int steps[N][N];// 记录步数 初始化为-1
typedef pair<int,int> PII;
PII prev[N][N];// 若需打印路径 则需要存父点
int BFS(int n,int m){
memset(steps,-1,sizeof(steps));
steps[0][0]=0;
queue<PII>Q;
Q.push({0,0}); //先入队
while(!Q.empty()){
int row[4]={-1,0,1,0}; int col[4]={0,1,0,-1}; //考虑队首周围的元素
for(int i=0;i<4;i++){
int x=Q.front().first+row[i];
int y=Q.front().second+col[i];
if(x>=0&&x<n&&y>=0&&y<m&&mp[x][y]==0&&steps[x][y]==-1){
steps[x][y]=steps[Q.front().first][Q.front().second]+1; //更新步数
Q.push({x,y});
if(x==n-1&&y==m-1) return steps[n-1][m-1];
}
}
Q.pop(); //出队
}
}
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>mp[i][j];
}
}
cout<<BFS(n,m);
}
“最少”->BFS 思路:把状态看成图里的一个结点 状态转移看成图里的边
八数码
本题的状态是:把每个二维数组flatten成string 注意转化的代码
#include<bits/stdc++.h>
using namespace std;
void BFS(string start){
queue<string> Q;
Q.push(start);
unordered_map<string,int> d;
d[start]=0;
string end="12345678x";
int axis_x[4]={-1,0,1,0}; int axis_y[4]={0,-1,0,1};
while(!Q.empty()){
string t=Q.front();
Q.pop(); 立即pop()
int father_dis=d[t]; 预存一下距离
int father=t.find('x'); 找到x的一维坐标
int fatherx=father/3; int fathery=father%3; 一维坐标转换二维坐标
for(int i=0;i<4;i++){
int sonx=fatherx+axis_x[i];
int sony=fathery+axis_y[i];
if(sonx>=0&&sonx<=2&&sony>=0&&sony<=2){
int son=3*sonx+sony; 二维坐标转换一维坐标
swap(t[father],t[son]);
if(!d.count(t)){ 这个状态未被考虑过
d[t]=father_dis+1;
Q.push(t);
}
swap(t[father],t[son]); 还原父串
}
}
}
if(!d.count(end)){ start无法变成end
cout<<"-1";
return ;
}
cout<<d[end];
}
int main(){
char c;
string start;
for(int i=0;i<9;i++){
cin>>c;
start+=c;
}
BFS(start);
}
树和图的存储 一般用邻接表 注意下邻接表的插入
树和图的DFS 代码都一样 需要st[N]存是否走过
本题为树的DFS e[i]存的是结点编号,核心:利用DFS向上传递结点数
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=N*2; // N个结点 无向树2(n-1)条边 那么链表里最多2(n-1)个结点 那么直接取2*N即可
int head[N],e[M],ne[M],idx; // 邻接表
int st[N];
int answer=N;
int n; // 一共有n个结点
void insert(int a,int b){ //编号a的结点到编号b有边
e[idx]=b;
ne[idx]=head[a];
head[a]=idx++;
}
int DFS(int u){
st[u]=1; 访问到了
int sum=1, max_son=0;
for(int i=head[u];i!=-1;i=ne[i]){
int number=e[i];
if(!st[number]){ 未被访问过
int num_son=DFS(number);
if(num_son>max_son) max_son=num_son;
sum+=num_son;
}
}
int remnant=n-sum;
int result=max(remnant,max_son);
answer=min(answer,result);
return sum;
}
int main(){
memset(head,-1,sizeof(head)); // 初始化头结点指空(-1)
cin>>n; int m=n;
m--;
while(m--){
int a,b;
cin>>a>>b;
insert(a,b); insert(b,a); // 无向树
}
DFS(1); //从1~n任意一个开始DFS都可以
cout<<answer;
}
树和图的BFS 代码都一样(无论有无环、重边) 和DFS一样也需要存是否走过
本题为图的BFS d[n]存距离的模板 也正好可以判断是否走过
因为点间距离为1 所以可以用BFS求最小值
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int head[N],e[N],ne[N],idx; 1<=n,m<=1e5
int d[N]; 初始化为全-1 -1则未访问到!
int n;
void insert(int a,int b){
e[idx]=b;
ne[idx]=head[a];
head[a]=idx++;
}
int BFS(int u){
d[u]=0;
queue<int> Q;
Q.push(u);
while(!Q.empty()){
int father=Q.front();
for(int i=head[father];i!=-1;i=ne[i]){
int son=e[i];
if(d[son]==-1){ //
d[son]=d[father]+1;
Q.push(son);
}
}
Q.pop();
}
return d[n];
}
int main(){
memset(head,-1,sizeof(head));
memset(d,-1,sizeof(d));
int m;
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
insert(a,b);
}
cout<<BFS(1);
}
有向图的拓扑序列
拓扑排序,是对一个 有向无环图(Directed Acyclic Graph简称DAG) G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
题目:求给定图的拓扑序列 若不存在输出-1
注意细节:
点的范围是1~n 而不是0~n-1
记得初始化head[]数组
顶点数n一般放全局
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int head[N],e[N],ne[N],idx;
int d[N]; 【存储】每个点的入度
int n; 顶点数 放全局 因为具体操作可能要遍历顶点
void insert(int a,int b){
e[idx]=b;
ne[idx]=head[a];
head[a]=idx++;
}
void top_sort(){
vector<int> answer; 存答案
priority_queue<int,vector<int>,greater<int>> t;
for(int i=1;i<=n;i++){ 下标!!!!
if(d[i]==0) t.push(i);
}
while(!t.empty()){
int father=t.top();
answer.push_back(father);
t.pop(); 这个pop()必须放这里!!!!!
for(int i=head[father];i!=-1;i=ne[i]){
int son=e[i];
d[son]--;
if(d[son]==0) t.push(son);
}
}
if(answer.size()!=n){ 有自环 不存在拓扑序列
cout<<"-1";
return ;
}
for(int i=0;i<answer.size();i++){
cout<<answer[i]<<" ";
}
}
int main(){
memset(head,-1,sizeof(head));
int m;
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
insert(a,b);
d[b]++;
}
top_sort();
}