习题课-深搜

山峰和山谷

题目描述

FGD小朋友特别喜欢爬山,在爬山的时候他就在研究山峰和山谷。
为了能够对旅程有一个安排,他想知道山峰和山谷的数量。
给定一个地图,为FGD想要旅行的区域,地图被分为 n×n 的网格,每个格子 (i,j) 的高度 w(i,j) 是给定的。
若两个格子有公共顶点,那么它们就是相邻的格子,如与 (i,j) 相邻的格子有(i−1,j−1),(i−1,j),(i−1,j+1),(i,j−1),(i,j+1),(i+1,j−1),(i+1,j),(i+1,j+1)。
你的任务是,对于给定的地图,求出山峰和山谷的数量,如果所有格子都有相同的高度,那么整个地图即是山峰,又是山谷。

输入描述

第一行包含一个正整数 n,表示地图的大小(1<=n<=1000)。
接下来一个 n×n 的矩阵,表示地图上每个格子的高度 w(0<=w<=1000000000)。

输出描述

共一行,包含两个整数,表示山峰和山谷的数量。

样例输入
5
8 8 8 7 7
7 7 8 8 7
7 7 7 7 7
7 8 8 7 8
7 8 8 8 8
样例输出
2 1
提示

山峰定义为:一个连通块,对于每一个属于此连通块的点,有:这个点周围所有与它八连通的点值都比这个点的值要小
山谷定义为:一个连通块,对于每一个属于此连通块的点,有:这个点周围所有与它八连通的点值都比这个点的值要大。 

代码 
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e3+10;
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
int f1,f2,c1,c2;
int a[N][N],n,vis[N][N];
void dfs(int x,int y){
	vis[x][y]=1;
	for(int i=0;i<8;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=n){
			if(a[nx][ny]<a[x][y]){
				f1=1;
			} 
			if(a[nx][ny]>a[x][y]){
				f2=1;
			}
			if(vis[nx][ny]==0&&a[nx][ny]==a[x][y]){
				dfs(nx,ny);
			}
		}
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(vis[i][j]==0){
				f1=0;
				f2=0;
				dfs(i,j);
				if(f1==1&&f2==0){
					c1++;
				}
				if(f1==0&&f2==1){
					c2++;
				}
				if(f1==0&&f2==0){
					c1++;
					c2++;
				}
			}
		}
	}
	cout<<c1<<" "<<c2;
	return 0;
}

沼泽问题

题目描述

达达要穿过一片N*N(2<=N<10)的沼泽地,入口和出口分别在左上角和右下角。沼泽地中的路分别放0和1,0表示可以走的道路,1表示不能走的泥沼,入口和出口处肯定是0。沼泽地行走的规则如下所示:即从某点开始,有四个方向可走,前进方格中数字为0时表示可通过,为1时表示不可通过,要另找路径。 找出所有从入口(左上角)到出口(右下角)的路径(不能重复),输出路径总数,如果无法到达,则输出0。

输入描述

第一行输入一个整数N,代表沼泽的的边长。

第2-N+1行输入每行输入N个整数(0 1),用来描述沼泽地地形

输出描述

一个整数,代表所有不重复的路径数量

样例输入
3
000
010
000
样例输出
2
代码
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=15;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int n,vis[N][N],cnt;
char a[N][N];
void dfs(int x,int y){
	vis[x][y]=1;
	if(x==n&&y==n){
		cnt++;
		vis[x][y]=0;
		return ;
	}
	for(int i=0;i<4;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&vis[nx][ny]==0&&a[nx][ny]=='0'){
			dfs(nx,ny);
			vis[nx][ny]=0;
		}
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
		}
	}
	dfs(1,1);
	cout<<cnt;
	return 0;
}

走迷宫

题目描述

有一个n*m格的迷宫(表示有n行、m列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这n*m个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。现在要你编程找出所有可行的道路,要求所走的路中没有重复的点,走时只能是上下左右四个方向。如果一条路都不可行,则输出相应信息(用-l表示无路)。

请统一用左上右下的顺序拓展,也就是 (0,-1),(-1,0),(0,1),(1,0)

输入描述

第一行是两个数n,m( 1 < n , m < 15 ),接下来是n行m列由1和0组成的数据,最后两行是起始点和结束点。 

输出描述

所有可行的路径,描述一个点时用(x,y)的形式,除开始点外,其他的都要用“->”表示方向。 
如果没有一条可行的路则输出-1。

样例输入
5 6
1 0 0 1 0 1
1 1 1 1 1 1
0 0 1 1 1 0
1 1 1 1 1 0
1 1 1 0 1 1
1 1
5 6
样例输出
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(2,4)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(3,4)->(4,4)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(2,4)->(2,5)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(3,4)->(3,5)->(4,5)->(5,5)->(5,6)
(1,1)->(2,1)->(2,2)->(2,3)->(3,3)->(4,3)->(4,4)->(4,5)->(5,5)->(5,6)
提示

【算法分析】 
  用一个a数组来存放迷宫可走的情况,另外用一个数组b来存放哪些点走过了。每个点用两个数字来描述,一个表示行号,另一个表示列号。对于某一个点(x,y),四个可能走的方向的点描述如下表: 
   2 
1  x,y  3 
   4 
  对应的位置为:(x, y-1),(x-1, y),(x, y+1),(x+1, y)。所以每个点都要试探四个方向,如果没有走过(数组b相应的点的值为0)且可以走(数组a相应点的值为1)同时不越界,就走过去,再看有没有到达终点,到了终点则输出所走的路,否则继续走下去。 
  这个查找过程用search来描述如下: 
procedure search(x, y, b, p);{x,y表示某一个点,b是已经过的点的情况,p是已走过的路} 
 begin 
   for i:=1 to 4 do{分别对4个点进行试探} 
   begin 
     先记住当前点的位置,已走过的情况和走过的路; 
     如果第i个点(xl,y1)可以走,则走过去; 
     如果已达终点,则输出所走的路径并置有路可走的信息, 
     否则继续从新的点往下查找search(xl,y1,b1,p1); 
   end; 
 end; 
  有些情况很明显是无解的,如从起点到终点的矩形中有一行或一列都是为0的,明显道路不通,对于这种情况要很快地“剪掉”多余分枝得出结论,这就是搜索里所说的“剪枝”。从起点开始往下的一层层的结点,看起来如同树枝一样,对于其中的“枯枝”——明显无用的节点可以先行“剪掉”,从而提高搜索速度。  

代码
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=15;
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
int n,vis[N][N],cnt,c,r,cc,rr,f=0,m;
int a[N][N];
struct node{
	int x,y;
}p[250];
void dfs(int x,int y,int step){
	vis[x][y]=1;
	p[step].x=x;
	p[step].y=y;
	if(x==cc&&y==rr){
		f=1;
		vis[x][y]=0;
		printf("(%d,%d)",c,r);
		for(int i=2;i<=step;i++){
			printf("->(%d,%d)",p[i].x,p[i].y);
		}
		cout<<endl;
		return ;
	}
	for(int i=0;i<4;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&vis[nx][ny]==0&&a[nx][ny]==1){
			dfs(nx,ny,step+1);
			vis[nx][ny]=0;
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	cin>>c>>r>>cc>>rr;
	dfs(c,r,1);
	if(f==0){
		cout<<-1;
	}
	return 0;
}

蚂蚁移动问题

题目描述

桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。

小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁的右脚弄伤了,于是蚂蚁只能向上或向右移动。小明把这只蚂蚁放在左下角的方格中,蚂蚁从左下角的方格中移动到右上角的方格中,每步移动一个方格。蚂蚁始终在方格矩阵内移动,请计算出不同的移动路线的数目。   

对于1行1列的方格矩阵,蚂蚁原地移动,移动路线数为1;对于1行2列(或2行1列)的方格矩阵,蚂蚁只需一次向右(或向上)移动,移动路线数也为1……对于一个2行3列的方格矩阵,如下图所示:  

蚂蚁一共有3种移动路线:

路线1:(1,1) - (1,2) - (1,3) - (2,3)

路线2:(1,1) - (1,2) - (2,2) - (2,3)

路线3:(1,1) - (2,1) - (2,2) - (2,3)

输入描述

输入只有一行,包括两个整数m和n(0 < m+n ≤ 20),代表方格矩阵的行数和列数,m、n之间用空格隔开。

输出描述

输出若干行,每行一个移动路线,输出形式如样例所示。

(为保证输出一致,蚂蚁移动时先向右,再向上)

样例输入
2 3
样例输出
蚂蚁共有3种移动路线:
路线1:(1,1) - (1,2) - (1,3) - (2,3)
路线2:(1,1) - (1,2) - (2,2) - (2,3)
路线3:(1,1) - (2,1) - (2,2) - (2,3)
代码 
#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=25;
int dx[2]={0,1};
int dy[2]={1,0};
int n,vis[N][N],cnt,m;
int a[N][N];
struct node{
	int x,y;
}p[25],ans[500][25];
void dfs(int x,int y,int step){
	vis[x][y]=1;
	p[step].x=x;
	p[step].y=y;
	if(x==n&&y==m){
		vis[x][y]=0;
		cnt+=1;
		for(int i=2;i<=step;i++){
			ans[cnt][i]=p[i];
		}
		return ;
	}
	for(int i=0;i<2;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&vis[nx][ny]==0){
			dfs(nx,ny,step+1);
			vis[nx][ny]=0;
		}
	}
}
int main(){
	cin>>n>>m;
	dfs(1,1,1);
	printf("蚂蚁共有%d种移动路线:\n",cnt);
	for(int i=1;i<=cnt;i++){
		printf("路线%d:(1,1)",i);
		for(int j=2;j<=n+m-1;j++){
			printf(" - (%d,%d)",ans[i][j].x,ans[i][j].y);
		}
		printf("\n"); 
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值