| 一、DFS? ? ? ? 主要思想:DFS是一种深度优先图遍历算法,当遍历的当前结点还存在子结点时,就去遍历它的子结点,直到没有子结点,即遍历到叶子结点时,再返回上一层寻找其他没有遍历过的子结点,若存在则进入该子结点对应的分支,不断向下寻找,若不存在再往上一层走,直到回到根结点为止。因此,DFS可以看做“从左往右”地遍历一个图或树,主要借助一个存储当前结点是否遍历过的bool数组st[]实现。在实际问题中,可以在遍历前对约束函数进行判断,如果满足再往下遍历,如果不满足则直接返回上一层,进行“剪枝”,减少不必要的遍历,缩短搜索时间。 ? ? ? ? 例题1 完全序列:给定一个数n,输出所有从1到n的n个数不重复的序列。 #include<iostream>
const int N=10;
int path[N],n;//path数组存储遍历结果
bool st[N];//st数组存储第i个数是否已经用过
void dfs(int u){
	if(u==n){//遍历到第n个数,即遍历到叶子结点,有一种答案
		for(int i=0;i<n;i++) printf("%d ",path[i]);
		printf("\n");
		return;
	}
	for(int i=1;i<=n;i++){//数字从1开始
		if(!st[i]){//第i个数没有被用过
			path[u]=i;
			st[i]=true;
			dfs(u+1);
			st[i]=false;//还原现场,为下一种答案的遍历做准备
		}
	}
}
int main(){
	scanf("%d",&n);
	
	dfs(0);//从根节点开始查找
	
	return 0;
}
 ? ? ? ? 例题2 八皇后问题:在一个n*n的棋盘上放置n个皇后,保证每两个皇后彼此之间不在同一行、同一列、同一对角线,输出所有可能的答案。 #include<iostream>
const int N=20;//对角线有2n个
char g[N][N];//存储棋盘结果
int n;
bool row[N],col[N],dg[N],udg[N];//依次存储行、列、左对角线、右对角线是否已有皇后
void dfs1(int u){//第一种遍历方法,按行进行dfs
	if(u==n){//最后一行也找到了满足的位置,进行答案输出
		for(int i=0;i<n;i++) puts(g[i]);
		printf("\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;
			dfs1(u+1);
			col[i]=dg[u+i]=udg[n-u+i]=false;//还原现场
			g[u][i]='.';
		}
	}
}
void dfs2(int x,int y,int s){//第二种遍历方法,按棋盘格进行dfs
	if(y==n){//已经走到最后一列
		y=0;//列数从0开始
		x++;//进入下一行
	}
	if(x==n){//走到最后一行
		if(s==n){//找到了满足个数的位置
			for(int i=0;i<n;i++) puts(g[i]);
			printf("\n");			
		}
		return;//没有足够个数的位置也要返回才能重新找,return必须在if外面
	}
	dfs2(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;
			dfs2(x,y+1,s+1);
			row[x]=col[y]=dg[x+y]=udg[x-y+n]=false;//还原现场
			g[x][y]='.';
	}
}
int main(){
	scanf("%d",&n);
	
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++) g[i][j]='.';
	}
	dfs2(0,0,0);
	
	return 0;
}
 二、BFS? ? ? ? 主要思想:BFS是一种广度优先图遍历算法。当前结点还有其他未遍历到的邻接结点时,遍历该邻接结点,直到没有邻接结点再遍历子结点,子结点的邻接结点都遍历完以后,回过头找父结点的邻接结点中未遍历的子结点,直到“一层”结点都遍历完,再去下一层。因此,BFS可以看做“从上到下”地遍历一个图或树,主要借助队列实现,一个起始结点的边都遍历完后就从队头弹出,作为边的终点被遍历到的结点都作为邻接点从队尾进入,等待遍历。在实际应用中,如果边的权重为1,或者通过在两点之间插入n个结点模拟边的权重n,BFS也可以解决最短路径问题。 ? ? ? ? 例题 迷宫:给出一个n*m的矩阵模拟迷宫,“0”代表通路,“1”代表墙,找出从迷宫左上角到迷宫右下角的最短路径。 #include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10;
typedef pair<int,int> PII;//(x,y)坐标
int g[N][N];//存储迷宫
int d[N][N];//存储路径长度
PII q[N*N];//bfs遍历中用到的队列
int hh,tt,n,m;
int dfs(){
	hh=tt=0;
	q[0]={0,0};//从左上角开始遍历,左上角坐标入队列
	
	memset(d,-1,sizeof(d));//路径长度初始化
	d[0][0]=0;
	
	int dx[4]={0,0,-1,1};//一个点的上下左右
	int dy[4]={-1,1,0,0};
	
	while(hh<=tt){
		auto t=q[hh++];//从队头弹出元素进行边的遍历
		for(int i=0;i<4;i++){//遍历上下左右四个相邻点
			int x=t.first+dx[i];
			int y=t.second+dy[i];
			
			if(x>=0&&x<n&&y>=0&&y<m&&d[x][y]==-1&&g[x][y]==0){//该相邻点合法且未被遍历过
				d[x][y]=d[t.first][t.second]+1;//路径长度在原来的基础上+1
				q[++tt]={x,y};//作为边的终点入队列等待遍历
			}
		}
	}
	return d[n-1][m-1];//返回右下角坐标对应的路径长度
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>g[i][j];
		}
	}
	cout<<dfs()<<endl;
	return 0;
}
 三、图的存储? ? ? ? 主要思想:图的存储主要有两种方法,一种是用二维数组存储邻接矩阵,这种方法在稀疏情况下浪费内存,另一种是邻接表,类似与构建哈希表的“拉链法”,边的起点作为一个链表的表头,边的终点作为链表中的元素,这样定义n个链表,就可以存储n个结点构成的图。无向图则在插入边时add(a,b),add(b,a)插入两遍。 ????????例题 拓扑序列:给出n个结点,m条边构建的图,判断该图是否有环,如果没有则输出拓扑序列,如果有则输出-1。 #include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010,M=2*N;
int h[N],e[M],ne[M],idx;//数组模拟链表
bool st[M];
int q[N],d[N];//q为bfs遍历所需队列,d为每个结点的入度
int n,m;
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);
	}
}
//在bfs遍历的基础上添加入度判断
bool topsort(){
	int hh=0,tt=-1;
	for(int i=1;i<=n;i++){//找到入度为0的点加入队列
		if(!d[i]) q[++tt]=i;
	}
	while(hh<=tt){//bfs遍历
		int t=q[hh++];
		for(int i=h[t];i!=-1;i=ne[i]){
			int j=e[i];
			d[j]--;//遍历完一条边删除一条边,即终点的入度-1
			if(d[j]==0) q[++tt]=j;//入度为0则加入队列等待遍历
		}
	}
	return tt==n-1;//如果所有点最终都入度为0即为无环
}
int main(){
	cin>>n>>m;
	memset(h,-1,sizeof(h));
	for(int i=0;i<m;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
		d[b]++;//构建一条边就给终点入度+1
	}
	if(topsort()){
		for(int i=0;i<n;i++) printf("%d ",q[i]);//进入队列的顺序即为图的拓扑序列
		printf("\n");
	}
	else printf("-1\n");
	
	return 0;
} 
 |