DFS和BFS 树和图的存储及其DFS BFS 有向图拓扑序列

在这里插入图片描述

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();
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值