基础算法学习笔记与解析之简单搜索

最近在学习搜索,将题目和想法整理下来,以便以后复习用。

现在题目也没有做完,以后边做边补吧。

首先先上题目地址:https://vjudge.net/contest/317154 ([kuangbin带你飞]专题一 简单搜索)

A - 棋盘问题

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

Input:

输入含有多组测试数据.
每组数据的第一行是两个正整数, n n n k k k ,用一个空格隔开,表示了将在一个 n ∗ n n*n nn的矩阵内描述棋盘,以及摆放棋子的数目。 n &lt; = 8 , k &lt; = n n &lt;= 8 , k &lt;= n n<=8,k<=n
当为 − 1 , − 1 -1 ,-1 1,1时表示输入结束。
随后的 n n n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . . .表示空白区域(数据保证不出现多余的空白行或者空白列)。

Onput:

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证 C &lt; 2 31 C&lt;2^{31} C<231)。

Sample Input:

2 1
#.
.#
4 4
…#
…#.
.#…
#…
-1 -1

Sample Output

2
1

解题思路:有很多这种类似的题,我们先按列放,每放一个子,将这一行进行标记,继续放下一列。若能把棋子全部放完,res+1。这个题要注意的是DFS结束的条件是旗子数目等于规定数目就跳出,千万不要理解成层数等于规定数目跳出,要区别一下八皇后问题。
既然想明白了我们直接上代码:

#include<iostream>
#include<cstring>
using namespace std;
int n,k,res,num;
char mapp[10][10];
int vis[10];
void dfs(int x,int num){//num是现在放的旗子数目,x是行数
	if(num==k){
		res++;
		return ;
	}
	if(x>=n) return ;
	else {
		for(int j=0;j<n;j++){
			if(vis[j]==0&&mapp[x][j]=='#'){
				vis[j]=1;//标记
				dfs(x+1,num+1);//搜索下一行,旗子数目+1
				vis[j]=0;//回溯,将标记清空
			}
		}
	}
	dfs(x+1,num);//下一次搜索
}
int main(){
	while(~scanf("%d%d",&n,&k)&&n!=-1&&k!=-1){
		memset(vis,0,sizeof(vis));
		res=0,num=0;
		for(int i=0;i<n;i++){
			scanf("%s",mapp[i]);
		}
		dfs(0,0);
		printf("%d\n",res);
	}
}
作为一个做好事不留名的“红领巾”,我们决定买一送一。我们顺便说一下八皇后问题;

传送门:https://www.luogu.org/problem/P1219

题目:

检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
在这里插入图片描述
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
//以下的话来自usaco官方,不代表洛谷观点
特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆USACO Training的帐号删除并且不能参加USACO的任何竞赛。我警告过你了!(你要是打表其实也行(小声比比)

输入格式

一个数字 N ( 6 &lt; = N &lt; = 13 ) N(6 &lt;= N &lt;= 13) N(6<=N<=13) 表示棋盘是 N ∗ N N * N NN大小的.

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入:

6

输出:

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

解题思路:跟上面那个题一样,先选定一行进行标记,标记行的同时,将列和主副对角线也一起标记了,再按照题目要求的进行输出就行了,主要是一个搜索和回溯的思想,去枚举,不符合题目规定就进行回溯。
AC代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int n,i;
int res;
int a[100],vis[5][100];
void dfs(int tot){	
	if(tot>n){
 		res++;//标记现有的方按数
  		if(res<=3){
   			for(i=1;i<=n;i++){
    			cout<<a[i]<<" ";
   			}
   			cout<<endl; 
  		}
  		return ;//按要求输出
 	}
 	else{
	  	for(int j=1;j<=n;j++){//列
	   		if((!vis[0][j])&&(!vis[1][j+tot])&&(!vis[2][tot-j+n])){//这一行代码非常重要,我们下面将
	    		a[tot]=j;//记录每一个皇后的位置
	    		vis[0][j]=1;
	    		vis[1][j+tot]=1;
	    		vis[2][tot-j+n]=1;//进行标记
	    		dfs(tot+1);//递归下一个皇后的搜索
	    		vis[0][j]=0;
	    		vis[1][j+tot]=0;
	    		vis[2][tot-j+n]=0;//回溯取消标记
	   		}
	  	}   
	 }
}
int main(){
 	cin>>n;
 	dfs(1);
 	cout<<res<<endl;
}

我们来介绍一下如何标记主副对角线。对于主对角线来说,主对角线上面的值为 x + y x+y x+y,例如这个数为 ( 1 , 1 ) , (1,1), (1,1)很明显,主对角线下一个点为 ( 2 , 2 ) , (2,2), (2,2),行数我们已经标记了,那么列数就是如上 j + t o t , j+tot, j+tot副对角线为 x − y , x-y, xy,但是由于这个值可能为负值,我们可以记录为 x − y + n 。 x-y+n。 xy+n


B - Dungeon Master

You are trapped in a 3D dungeon and need to find the quickest way out! The dungeon is composed of unit cubes which may or may not be filled with rock. It takes one minute to move one unit north, south, east, west, up or down. You cannot move diagonally and the maze is surrounded by solid rock on all sides.
Is an escape possible? If yes, how long will it take?

Input:

The input consists of a number of dungeons. Each dungeon description starts with a line containing three integers L, R and C (all limited to 30 in size).
L is the number of levels making up the dungeon.
R and C are the number of rows and columns making up the plan of each level.
Then there will follow L blocks of R lines each containing C characters. Each character describes one cell of the dungeon. A cell full of rock is indicated by a ‘#’ and empty cells are represented by a ‘.’. Your starting position is indicated by ‘S’ and the exit by the letter ‘E’. There’s a single blank line after each level. Input is terminated by three zeroes for L, R and C.

Output:

Each maze generates one line of output. If it is possible to reach the exit, print a line of the form Escaped in x minute(s). where x is replaced by the shortest time it takes to escape.
If it is not possible to escape, print the line Trapped!

Sample Input:

3 4 5
S…
.###.
.##…
###.#
#####/
#####/
##.##/
##…/
#####/
#####/
#.###
####E
1 3 3
S##
#E#
###/
0 0 0

Sample Output

Escaped in 11 minute(s).
Trapped!

解题思路:一个三位BFS比较裸的题,一共6个方向,我们一个一个搜就行了。
代码如下:

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
ll sx,sy,sz,ex,ey,ez;
ll l,r,c,i,j,k;
ll vis[100][100][100];
char mapp[100][100][100];
ll dir[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
struct code{
	ll x,y,z;
	ll step;
};
ll bfs(){
	queue<code> Q;
	code now,next;
	now.x=sx;
	now.y=sy;
	now.z=sz;
	now.step=0;
	vis[sx][sy][sz]=1;
	Q.push(now);
	while(!Q.empty()){
		now=Q.front();
		Q.pop();
		if(now.x==ex&&now.y==ey&&now.z==ez){
			return now.step;
		}
		for(i=0;i<6;i++){
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
			next.z=now.z+dir[i][2];
			if(next.x<0||next.y<0||next.z<0||next.x>=l||next.y>=r||next.z>=c||vis[next.x][next.y][next.z]==1||mapp[next.x][next.y][next.z]=='#')
			continue;
			next.step=now.step+1;
			vis[next.x][next.y][next.z]=1;
			Q.push(next);
		}
	} 
	return 0;
}
int main(){
	while(~scanf("%lld%lld%lld",&l,&r,&c)&&l!=0&&r!=0&&c!=0){
		memset(vis,0,sizeof(vis));
		for(i=0;i<l;i++){
			for(j=0;j<r;j++){
				scanf("%s",&mapp[i][j]);
				for(k=0;k<c;k++){	
					if(mapp[i][j][k]=='S'){
						sx=i;
						sy=j;
						sz=k;
					}
					else if(mapp[i][j][k]=='E'){
						ex=i;
						ey=j;
						ez=k;
					}
				}
			}	
		}
		ll res=0;
		res=bfs();
		if(res)	printf("Escaped in %lld minute(s).\n",res);
		else printf("Trapped!\n");		
	} 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值