深搜与方格三个例题

一、剪邮票问题

题目描述在这里插入图片描述

解题思路

把12选5的各种选法排列出来,并判断是不是5格连着。
判断方法:把所有的红格子设为1,其它设置为0,在红格子中随便选一个(后面代码具体实现,用的都是最后一个红格子,这无所谓),从这个格子出发,按照它的周围4个方向尝试一遍,一个格子被走过立即设置为0,看走完后是不是走了5个。

代码

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int a[5][5]; //二维数组存储方格图
//dx与dy配合使用进行方格间的移动操作
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int dfs(int x, int y)//参数为该点的坐标
{
	int	ans = 1;
	a[x][y] = 0; //走过该点置为0
	for(int i = 0; i < 4; i++){ //分别走到上、右、下、左点
		int nextx = x + dx[i]; //下一个点的横坐标
		int nexty = y + dy[i]; //下一个点的纵坐标
		if(nextx >= 0 && next < 3 && nexty >= 0 && nexty < 4 && a[nextx][nexty]){ //判断下标是否越界并是否为1
			ans += dfs(nextx, nexty);
		}
		
	}
	return ans;	
}
int main()
{
	int ans = 0;
	string s = "000000011111"; //用来表示每个方格的状态 0 或 1
	do{
		int start; //出发点
		//一维转二维并设最后一点为出发点
		for(int i = 0; i < s.length(); i++){
			a[i/4][i%4] = (s[i] == '1' ? 1 : 0); 
			if('1' == s[i])
				start = i;
		}
		//以出发点做起始进行深搜
		if(dfs(start / 3, start % 4) == 5)
			ans++;	
	}while(next_permutation(s,begin(), s.end()));
	cout << ans << endl;
}

二、方格对称问题

题目描述

在这里插入图片描述

解题思路

根据题目要求,我们从中心位置开始,向边界做深搜。为了保证对称,在dfs过程中,每一步都标记两个位置,当前位置和关于中心对称的位置,直到走到6*6大方格的最外侧,记录多少种走法。由于旋转对称的需要,最终答案是关于上述走法除以4.

代码

#include <iostream>
int N = 6;
int a[N][N];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int ans;
void dfs(int x, int y)
{
	//到达边界则使结果加一并返回
	if(x == 0 || x == N || y == 0 || y == N){ 
		ans++;
		return;
	}
	
	for(int i = 0; i < 4; i++){
		int nextx = x + dx[i];
		int nexty = y + dy[i];
		if(a[nextx][nexty]) //如果下一点点已经被遍历过,则continue
			continue;
		a[nextx][nexty] = 1; //下一点设为1
		a[N - nextx][N - nexty] = 1; //关于下一点对称的点设为1
		dfs(nextx, nexty);
		//恢复操作便于下一轮遍历
		a[nextx][nexty] = 0;
		a[N - nextx][N - nexty] = 0;
	}
	return;
}
int main()
{
	v[N / 2][N / 2] = 1;//设置出发点为1
	dfs(N / 2, N / 2);
	cout << ans / 4 << endl;
	return 0;
}

3.岛屿沉没(无向图的计数)

问题描述

你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:


.##…
.##…
…##.
…####.
…###.

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:





…#…

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。

照片保证第1行、第1列、第N行、第N列的像素都是海洋。

【输出格式】
一个整数表示答案。

【输入样例】
7

.##…
.##…
…##.
…####.
…###.

【输出样例】
1

解题思路

图的深搜,在每次深搜时每个节点只需访问一次,所以在深搜过程中不用返回状态

1.原岛屿的计数

以每个未访问过的#为起点,对相连的#进行深度搜索,同时改变该点的访问状态(visit [x][y] 设为true),结束后使cnt++,cnt即为沉没前岛屿的个数

2.岛屿沉没操作

遍历每块#,若#的上下左右都为#,则该点不沉没,使状态值 设为false。
遍历结束后,每块沉没的陆地状态值为true,在下面的深搜中不再被访问。

3.岛屿沉没的计数

计算出沉没后的岛屿数量(同原岛屿的计数),然后相减

代码

#include <iostream>
#include <vector>
using namespace std;
vector< vector<char> > a;
vector< vector<bool> >visit;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
void dfs(int x, int y)
{
	//如果该点为海洋或该点已经访问过返回
	if(a[x][y] == '.' || b[x][y] == true)
        return;
    b[x][x] = true;
    for(int k = 0; k < 4; k++){
		int nextx = x + dx[k];
		int nexty = y + dy[k];
		dfs(nextx, nexty);
    }
}
int main()
{
	int num;
	int cnt1 = 0, cnt2 = 0;
	cin >> num;
	a.resize(num, vector<char>(num));//二维vector的重构
	/*vector<vector<int> > v(r, vector<int>(c, 0));
	v.resize(n);
    for(int i = 0; i < n; i++)
        v[i].resize(n);
	*/
	for(int i = 0; i < num; i++){
        for(int j = 0; j < num; j++){
         	char c;
         	cin >> c;
        	a.push_back(c);
        }
     }
    /*错误的初始化一个int char[7][7]方式
    a = {{'.','.','.','.','.','.','.'},
         {'.','#','#','.','.','.','.'},
         {'.','#','#','.','.','.','.'},
         {'.','.','.','.','#','#','.'},
         {'.','.','#','#','#','#','.'},
         {'.','.','.','#','#','#','.'},
         {'.','.','.','.','.','.','.'}};
    正确的初始化一个int char[8][8]方式
    a = {"1234567",
    	 "2234567",
    	 "3234567",
    	 "4234567",
    	 "5234567",
    	 "6234567",
    	 "7234567",} */
    for(int i = 0; i < num; i++){
        for(int j = 0; j < num; j++){
            if(a[i][j] == '#' && b[i][j] == false)
            {
                cnt1++;
                dfs(i, j);
            }
        }
    }
    for(int i = 0; i < num; i++){
        for(int j = 0; j < num; j++){
            if(a[i][j] == '#'){
                 if(a[i - 1][j] == '#' && a[i][j + 1] == '#' && a[i + 1][j] == '#' && a[i][j - 1] == '#')
                    b[i][j] = false;
            }
        }
    }
    for(int i = 0; i < 7; i++){
        for(int j = 0; j < 7; j++){
             if(a[i][j] == '#' && b[i][j] == false){
                cnt2++;
                dfs(i, j);
            }
        }
    }
    cout << cnt1 - cnt2 << endl;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值