Search-DFS&BFS

1.DFS

非连通无向图

【模板题】一般都是给矩阵,两种不同字符表示不同含义。

比如下图,对连通的(8方向可走)的@字符组成的块进行搜索。

问题:连通的有几块?

#include<cstdio>
#include<cstring>
const int maxn = 100 + 5;

char pic[maxn][maxn];
int m, n, idx[maxn][maxn]; // visited数组

void dfs(int r, int c, int id) {
  if(r < 0 || r >= m || c < 0 || c >= n) return; // 走到边界
  if(idx[r][c] != 0 || pic[r][c] != '@') return; // 已被访问或进入非法区域
  idx[r][c] = id; // 标记已访问
  // 走8个方向 会有0,0的位移 但是无所谓
  for(int dr = -1; dr <= 1; dr++)
    for(int dc = -1; dc <= 1; dc++)
      if(dr != 0 || dc != 0) dfs(r+dr, c+dc, id);
}

int main() {
  while(scanf("%d%d", &m, &n) == 2 && m && n) {
    for(int i = 0; i < m; i++) scanf("%s", pic[i]);
    memset(idx, 0, sizeof(idx));
    int cnt = 0;
    // 遍历图 找未被访问的@
    for(int i = 0; i < m; i++)
      for(int j = 0; j < n; j++)
        if(idx[i][j] == 0 && pic[i][j] == '@') dfs(i, j, ++cnt); 
// 只要连通就一直找,不连通时退出dfs(),所以每在main里调用一次,就是找到了几个连通图
    printf("%d\n", cnt);
    
  }
  return 0;
}

输入

Each test case contains a single integer N (N < 1000), the length and also the width of Hoenn. Next N lines will be the result of the battle. "o" means a landmass and "a" means a sea. The input is terminated when N = 0.

输出

For each test case, you must print the size of the biggest connnected seas filled by Kyogre.

输入样例 1 

5

aaooo

oooao

oaaaa

aaaaa

ooooo

0

输出样例 1

10

问题:4方向(上下左右)可走,连通的a块最大字符个数是多少。

#include <bits/stdc++.h>
using namespace std;

char area[1005][1005];
int visited[1005][1005];
int n;
char tmp[1005];

void dfs(int i, int j, int *cnt){
	if( i<0 || i>=n || j<0 || j>=n )
		return;
	if( visited[i][j]==1 || area[i][j]!='a' )
		return;
	visited[i][j] = 1;
	(*cnt)++;
	dfs(i-1, j, cnt);
	dfs(i, j+1, cnt);
	dfs(i+1, j, cnt);
	dfs(i, j-1, cnt);
	
}

int main(){
	while( scanf("%d", &n) && n!=0 ){
		memset(area, 0, sizeof(area));
		memset(visited, 0, sizeof(visited));
		for(int i=0; i<n; i++){
			scanf("%s", tmp);
			for(int j=0; j<n; j++){
				area[i][j] = tmp[j];
			}
		}
		
		int cnt=0;
		int max = 0;
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				
				if( area[i][j]=='a' && visited[i][j]==0 ){
					cnt  = 0;
					dfs(i, j, &cnt);
				}
				if( cnt > max ){
					max = cnt;
				}
			}

		}
		
		printf("%d\n", max);
	}
}

连通有向图,用邻接矩阵表示(1到2,即第一行第二列为1)

 

拓展:拓扑排序 topological sorting

输出有向图1到n的一条路径 DFS(因为只找一条)

输入

There are several groups of input, and the first row in each group includes two numbers N (1<=N<=500), and M, where N represents the number of teams and M means there are M lines of input data following the first row of each group. In the next M rows, each row also includes two integers P1 and P2, meaning that team P1 wins against team P2.

输出

Give a ranking that meets the requirements. Output with a space between two team numbers and no space after the last one.

输入样例 1 

4 3

1 2

2 3

4 3

输出样例 1

1 2 4 3

提示

topological sorting

Other notes: The ranking may not be unique, and you need to output the smallest ranking in numerical order, that is, the team with a smaller number is required to output first. The input data are guaranteed to be correct, which means the input data ensure that there will be a ranking which meets the requirements.(同条件下,字典序小的在前面)

#include<iostream>
using namespace std;

int G[501][501];
int visited[501];// 访问优先级数组 -1表示已被访问 >=0 表示该顶点入度个数
// visited[]==0 表示入度为0
int ans[501]; // 1到n的路径 即拓扑排序结果
int n,m;

void topo(){
	int index = 1;
	for (int k = 0; k < n; k++){
		for (int i = 1; i <= n; i++){ // 遍历顶点 找入度为0的点
			// 从小到大遍历符合字典序
			if (visited[i] == 0){ 
				ans[index] = i;
				for (int j = 1; j <= n; j++){ // 找与i邻接的点
					if (G[i][j] == 1){
						visited[j]--; // i已被访问 删除其出度的边
                                      // 所以j的入度减少
					}
				}
				visited[i] = -1;
				index++;
				break;
			}
		}
	}
	
}
int main(){
	
	while (cin >> n >> m){
		int a, b;
		for (int i = 1; i <= n; i++){
			for (int j = 1; j <= n; j++){
				G[i][j] = 0;
			}
			visited[i] = 0;
		}
		for (int i = 0; i < m; i++){
			cin >> a >> b; // a到b有边
			if (G[a][b] == 0) // 不是重边
				visited[b]++;
            G[a][b] = 1; // (a,b)=1
			
		}// 如果一个顶点没有被指向过,那它一定是最大的
		topo();
		
		for (int i = 1; i < n; i++){
			cout << ans[i] << " ";
		}
		cout <<ans[n]<< endl;
	}
	return 0;
}

拓展:n皇后问题 n queens puzzle

n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击(任意两个皇后不能位于同一行,同一列,同一斜线)。问有多少种放法。

本质是n个数全排列问题。从第0行开始,对一行,枚举棋子放在每一列的情况。

回溯是一种优化的递归,它相当于深度优先,当某一个情况不满足要求时立即“剪枝” ——即退出当前栈,回到上一个递归的栈里。

// backtracking 递归完成回溯
//回溯通常比暴力枚举所有完整的候选项要快得多,因为它可以通过单个测试消除许多候选项。


#include <bits/stdc++.h>
using namespace std;

int C[50], tot = 0, n = 8, nc = 0;
// 一行仅一个皇后 可以将n*n的二维棋盘用一维数组表示
// C[i] 表示第i行的皇后放在第C[i]列
void search(int cur) {
	int i, j;
	nc++; // search函数调用次数
	if(cur == n) { // recursive boundary 
		// 递归到最后一行 找到了符合要求的一个摆法 计数+1
		tot++;
	} else for(i = 0; i < n; i++) { //在第cur行 查找皇后放在该行的每一列的情况
		int ok = 1;
		C[cur] = i;  // (cur,i) // 第cur行的皇后放在第i列
		for(j = 0; j < cur; j++)
			if(C[cur] == C[j] || cur-C[cur] == j-C[j] || cur+C[cur] == j+C[j]) { 
        // 同列 || 对角线
			ok = 0;
			break;
		}
		if(ok) search(cur+1);
	}
}

int main() {
	while( cin>>n ){
		tot = 0;
		search(0);
		printf("%d\n", tot);
	}
	return 0;
}

一点到另一点的路径条数 

DFS,找到终点总数加一。定点访问完了要复原。

	#include <bits/stdc++.h>
using namespace std;
 
int n,m;
char maze[7][7];
int visited[7][7];
char tmp[7];
int cnt;
 
void dfs(int row, int col){
	if( row<0 || row>=n || col<0 || col >=m ){
		return;
	}
	if( visited[row][col]==1 || maze[row][col]=='F' ){
		return;
	}
	// 当前单元可走
	visited[row][col] = 1;
	if( maze[row][col]=='E' ){
		cnt ++;
		visited[row][col] = 0;
		return;
	}
	
	dfs(row-1, col);
	dfs(row, col+1);
	dfs(row+1, col);
	dfs(row, col-1);
	visited[row][col] = 0;
}
 
 
int main(){
	while(scanf("%d %d", &n, &m)!=EOF ){
		
		int Bi = 0;
		int Bj = 0;
		for(int i=0; i<n; i++){
			scanf("%s", tmp);
			for(int j=0; j<m; j++){
				maze[i][j] = tmp[j];
				if( tmp[j]=='B' ){
					Bi = i;
					Bj = j;
				}
			}
		}
		cnt = 0;
		dfs(Bi, Bj);
		printf("%d\n", cnt);
		
	}
	
}

最短路径 边代价相等

连通 边代价不相等——需要用到迪杰斯特拉/旅行商(贪心经典问题)算法

每个顶点到另一个顶点代价相等——DFS/BFS/A*启发

BFS 最短路

#include <bits/stdc++.h>
using namespace std;
 
struct Node{
	int row,col;
	char c;
	
	int Mto; // M点到该点的最短距离
	int isVisited;
};
 
Node land[1002][1002];
Node *M;
Node *D;
char str[1002];
int N;
 
int newX[4] = {1, -1, 0, 0};
int newY[4] = {0, 0, 1, -1};
 
int BFS(){
	queue<Node*> openlist;
	
	openlist.push(M);
	M->Mto = 0;
	M->isVisited = 1;
	
	while(openlist.size()!=0){
		Node* curr = openlist.front();
		openlist.pop();
		
		if( curr==D ){
			return curr->Mto;
		}
		
		int i, j;
		// 以curr为中心 分别向上 右 下 左四个方向寻找
		for(int k=0; k<4; k++){
			i = curr->row + newX[k];
			j = curr->col + newY[k];
			if( i<0 || i>=N || j<0 || j>=N )
				continue;
			if( land[i][j].c=='#' || land[i][j].isVisited==1 )
				continue;
			Node *p = &land[i][j];
			openlist.push(p);
			p->Mto = curr->Mto + 1;
			p->isVisited = 1;
		}
	}
	return -1;
}
 
int main(){
	while(scanf("%d", &N)!=EOF && N!=0){
		// 初始化
		for(int i=0; i<N; i++){
			scanf("%s", str);
			for(int j=0; j<N; j++){
				land[i][j].c = str[j];
				if( land[i][j].c == 'M' ){
					M = &land[i][j];
				}
				else if( land[i][j].c == 'D'){
					D = &land[i][j];
				}
				land[i][j].row = i;
				land[i][j].col = j;
				land[i][j].Mto = 0;
				land[i][j].isVisited = 0;
			}
		}
		
		int res = BFS();
		if (res == -1)
			printf("%d\n", res);
		else
			printf("%d\n", res);
	}
	return 0;
}

DFS最短路

#include<iostream>
using namespace std;
int n;

int next_x, next_y;

int minn;
string graph[1001];
int d[4][2] = { {-1,0},{0,1},{1,0},{0,-1} };
int g[1001][1001];


void dfs(int x, int y, int step, int end_x, int end_y)  //step记录当前已经走过的步数 
{
	if (x == end_x && y == end_y)
	{
		if (step < minn)
			minn = step;
		return;
	}
	//找下一点坐标:水平垂直四种走法 
	for (int i = 0; i < 4; ++i)
	{
		next_x = x + d[i][0];
		next_y = y + d[i][1];
		//判断越界 
		if (next_x < 0 || next_y < 0 || next_x >= n || next_y >= n)
			continue;
		//判断是否可走
		/*if (graph[next_x][next_y] == 'D')
		  break;*/
		if (graph[next_x][next_y] == '@' && g[next_x][next_y] == 0)
		{
			g[next_x][next_y] = 1;
			dfs(next_x, next_y, step + 1, end_x, end_y);
			g[next_x][next_y] = 0; //回溯 
		}
		
	}
	return;
	
}

int main()
{
	while (cin >> n && n != 0)
	{
		//step = 0;
		minn = 9999;
		int end_x, end_y;
		int start_x, start_y;
		int flag = 0;
		//memset(g,0, 1001*1001*sizeof(int));
		for(int i = 0; i < n; ++i)
			for(int j = 0; j < n; ++j)
				g[i][j] = 0;
		for (int i = 0; i < n; ++i)
		{
			cin >> graph[i];
			for (int j = 0; j < n; ++j)
			{
				
				//记录起点终点 
				if (graph[i][j] == 'M')
				{
					start_x = i;
					start_y = j;
				}
				if (graph[i][j] == 'D')
				{
					end_x = i;
					end_y = j;
				}
			}
		}
		
		graph[start_x][start_y] = '@';
		graph[end_x][end_y] = '@';
		g[start_x][start_y] = 1;

			dfs(start_x, start_y, 0, end_x, end_y);
			if (minn == 9999)
				cout << -1 << endl;
			else
				cout << minn << endl;

		
	}
	return 0;
}

A*(OJ上一直是TLE的,但是样例都能过) 

#include <bits/stdc++.h>
using namespace std;

struct Node{
	int row,col;
	char c;
	
	int G; // 当前路径代价
	int H; // 预估路径代价 曼哈顿距离
	int F;
};
Node land[1001][1001];
char str[1001];
int N;
Node* M;
Node* D;
int len;
int isVisited=0;

bool A_star(){
	vector<Node*> openlist; //可寻路的顶点
	vector<Node*> closelist; //已被寻路的顶点
	
	M->H = abs(M->row - D->row) + abs(M->col - D->col);
	openlist.push_back(M);
	M->G = 0;
	M->F = M->H + M->G;
	
	while( openlist.size()!=0 ){
		Node* curr = NULL;
		// 本质是BFS 但由于包含代价可以减少枚举次数
		// 从可被寻路的顶点中选择F最小的
		for(vector<Node*>::iterator p=openlist.begin(); p!=openlist.end(); p++){
			if( curr==NULL ){
				curr = *p;
			}
			else if((*p)->F < curr->F){
				curr = *p;
			}
		}
//		cout << "curr->row: " << curr->row << endl;
//		cout << "curr->col: " << curr->col << endl;
		
		if( curr==D ){
			return true;
		}
		
		int i, j;
		// 以curr为中心 分别向上 右 下 左四个方向寻找
		if( (curr->row)-1 >= 0 ) { // 上可走
			i = curr->row-1;
			j = curr->col;
			Node *p = &land[i][j];
			if(land[i][j].c!='#'){
				isVisited = 0;
				for(vector<Node*>::iterator itr=closelist.begin(); itr!=closelist.end(); itr++){
					if(*itr==p ){
						isVisited = 1;
						break;
					}
				}
				if(isVisited==0 ) { //不在closelist的顶点
					p->H = abs(p->row - D->row) + abs(p->col - p->col);
					if( curr->G + 1 < p->G ){
						p->G = curr->G + 1;
						p->F = p->G + p->H;
						
						if(find(openlist.begin(), openlist.end(), p)==openlist.end()){
							openlist.push_back(p);
						}
					}
				}
			}
			
		}
		
		if( curr->col+1 < N ){ // 右可走
			i = curr->row;
			j = curr->col + 1;
			Node *p = &land[i][j];
			if(land[i][j].c!='#'){
				isVisited = 0;
				for(vector<Node*>::iterator itr=closelist.begin(); itr!=closelist.end(); itr++){
					if(*itr==p ){
						isVisited = 1;
						break;
					}
				}
				if(isVisited == 0){
					p->H = abs(p->row - D->row) + abs(p->col - p->col);
					if( curr->G + 1 < p->G ){
						p->G = curr->G + 1;
						p->F = p->G + p->H;
						
						if(find(openlist.begin(), openlist.end(), p)==openlist.end()){
							openlist.push_back(p);
						}
					}
				}
			}
			
		}
		
		if( curr->row+1<N ){ // 下可走
			i = curr->row+1;
			j = curr->col;
			Node *p = &land[i][j];
			if(land[i][j].c!='#'){
				isVisited = 0;
				for(vector<Node*>::iterator itr=closelist.begin(); itr!=closelist.end(); itr++){
					if(*itr==p ){
						isVisited = 1;
						break;
					}
				}
				if(isVisited==0 ){
					p->H = abs(p->row - D->row) + abs(p->col - p->col);
					if( curr->G + 1 < p->G ){
						p->G = curr->G + 1;
						p->F = p->G + p->H;
						
						if(find(openlist.begin(), openlist.end(), p)==openlist.end()){
							openlist.push_back(p);
						}
					}
				}
			}
			
		}
		
		if( curr->col-1>=0 ){ // 左可走
			i = curr->row;
			j = curr->col-1;
			Node *p = &land[i][j];
			if(land[i][j].c!='#'){
				isVisited = 0;
				for(vector<Node*>::iterator itr=closelist.begin(); itr!=closelist.end(); itr++){
					if(*itr==p ){
						isVisited = 1;
						break;
					}
				}
				if(isVisited == 0 ){
					p->H = abs(p->row - D->row) + abs(p->col - p->col);
					if( curr->G + 1 < p->G ){
						p->G = curr->G + 1;
						p->F = p->G + p->H;
						
						if(find(openlist.begin(), openlist.end(), p)==openlist.end()){
							openlist.push_back(p);
						}
					}
				}
			}
			
		}
		
		
		// 移除可访问列表中的curr
		openlist.erase(find(openlist.begin(), openlist.end(), curr));
		// 将curr加入已访问列表
		closelist.push_back(curr);
	}
	
	return false;
}
int main(){
	while(scanf("%d", &N)!=EOF && N!=0 ){
		
		len = 0;
		for(int i=0; i<N; i++){
			scanf("%s", str);
			for(int j=0; j<N; j++){
				land[i][j].c = str[j];
				if( land[i][j].c == 'M' ){
					M = &land[i][j];
				}
				else if( land[i][j].c == 'D'){
					D = &land[i][j];
				}
				land[i][j].row = i;
				land[i][j].col = j;
				land[i][j].G = 5000;
				land[i][j].F = 5000;
			}
		}
		// 初始化曼哈顿距离H 当前路径代价G和总代价F初始化为无穷大
//		for(int i=0; i<N; i++){
//			for(int j=0; j<N; j++){
//				land[i][j].H = abs(i - D->row) + abs(j - D->col);
//				land[i][j].G = numeric_limits<int>::max();
//				land[i][j].F = numeric_limits<int>::max();
//			}
//		}
		
		// A*查找
		if(A_star()){
			len = D->G;
			printf("%d\n", len);
		}
		else{
			len = -1;
			printf("%d\n", len);
		}
		
	}
	return 0;
}

 2. BFS

拓展:8皇后问题

在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。问有多少种摆法。

从第一行变化到第二行的状态,最少需要变换多少步,并打印每一步变换的过程。

输入样例 2 

2 6 4 1 3 7 0 5 8

8 1 5 7 3 6 4 0 2

// 8-puzzle encoding
#include<cstdio>
#include<cstring>
//#include<set>

//using namespace std;

typedef int State[9];  // define the state type 
const int MAXSTATE = 1000000;
State st[MAXSTATE], goal;  // the array of all states st[MAXSTATE]
int dist[MAXSTATE]; // the distance array
int prevState[MAXSTATE]; // 对于prevstate[i]:存储第i个栈的前一个状态(存的是下标)
int path[MAXSTATE];

// cantor expansion 康托展开 
int vis[362880], fact[9];   // 0~8 permutations -> 0~362879 (9!=362880)
void init_lookup_table() {
	fact[0] = 1;
	for(int i = 1; i < 9; i++) fact[i] = fact[i-1] * i;
}

int try_to_insert(int s) {
	int code = 0;            // map st[s] to code
	for(int i = 0; i < 9; i++) {
		int cnt = 0;
		for(int j = i+1; j < 9; j++) if(st[s][j] < st[s][i]) cnt++;
		code += fact[8-i] * cnt;
	}
	if(vis[code]) return 0;
	return vis[code] = 1;
}

const int dx[] = {-1, 1, 0, 0}; // up down left right
const int dy[] = {0, 0, -1, 1};
int bfs() {
	init_lookup_table();
	int front = 1, rear = 2;  // start from 1 (the index 0 is not used)
	prevState[1] = 1;
	while(front < rear) {
		State& s = st[front];  // use &s to simplify the code
		if(memcmp(goal, s, sizeof(s)) == 0) return front; // find the goal state, return; memcmp() is in cstring
		int z;
		for(z = 0; z < 9; z++) if(!s[z]) break;  // find the empty position, that is, 0
		int x = z/3, y = z%3;  // row && column (0~2)
		for(int d = 0; d < 4; d++) {
			int newx = x + dx[d];
			int newy = y + dy[d];
			int newz = newx * 3 + newy;  // the new 0 position
			if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {  // if the move is possible
				prevState[rear] = front;
				State& t = st[rear];
				memcpy(&t, &s, sizeof(s)); // expand a new state; memcpy s->t
				t[newz] = s[z]; // move 0
				t[z] = s[newz]; // move a number
				dist[rear] = dist[front] + 1;  // compute the new state's distance
				if(try_to_insert(rear)) rear++; // the insert succeeds, rear++ 
			}
		}
		front++; // after expansion, front++ 
	}
	return 0;  // failed
}

int main() {
	for(int i = 0; i < 9; i++){
		scanf("%d", &st[1][i]);
	}	
	for(int i = 0; i < 9; i++)
		scanf("%d", &goal[i]);
	
	
	int ans = bfs();
	
	
	if(ans > 0) printf("%d\n", dist[ans]);
	else printf("-1\n");
	
	path[0] = ans;
	int preIndex = prevState[ans];
	for(int i=1; i<dist[ans]+1; i++){
		path[i] = preIndex;
		preIndex = prevState[preIndex];
	}
	
	for(int i=dist[ans]; i>=0; i--){
		//printf("%d ", path[i]);
		for(int k=0; k<8; k++){
			printf("%d ", st[path[i]][k]);
		}
		printf("%d\n", st[path[i]][8]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值