DFS深度优先搜索刷题(二)

文章介绍了使用深度优先搜索(DFS)算法解决迷宫中的路径计数问题,涉及到了设置瓷砖状态、水格联通计数和棋盘放棋子的优化策略。通过递归遍历并更新状态,避免了回溯,提高了效率。
摘要由CSDN通过智能技术生成

目录

一.P1683 入门​编辑

二.P1596 [USACO10OCT] Lake Counting S 

 三.1114. 棋盘问题

四.P1025 [NOIP2001 提高组] 数的划分 


一.P1683 入门

算法思想:设置瓷砖状态st,这里瓷砖状态是否走过决定计数与否,因为可以重复走过但只记一次,所以可以不用回溯。每一次dfs都记录此时的坐标与进入可能的新坐标。


const int N = 25;

int W, H;
char map[N][N];//存地图
bool st[N][N];//存每个瓷砖走过状态
int res;//记录走过的瓷砖数
//记录4个方向
int dx[4] = { 1,-1,0,0 };
int dy[4] = { 0,0,1,-1 };


//当前访问到的坐标是(x,y)
void dfs(int x, int y)
{
	//模拟走迷宫
	for (int i = 0; i < 4; i++)
	{
		int a = x + dx[i], b = y + dy[i];//新坐标
		if (a < 0 || a >= H || b < 0 || b >= W) continue;//是否超出边界
		if (map[a][b] != '.') continue;//是否为坏瓷砖
		if (st[a][b]) continue; //是否已经走过这块瓷砖

		st[a][b] = true;//设置已走瓷砖的状态
		res++;
		dfs(a, b);
		/*这里可以重复走瓷砖但不多次计数,无需回溯*/
	}
}

int main()
{
	scanf("%d %d", &W, &H);
	for (int i = 0; i < H; i++)
		scanf("%s", map[i]);
	for (int x = 0; x < H; x++)
	{
		for (int y = 0; y < W; y++)
		{
			if (map[x][y] == '@')
			{
				st[x][y] = true;
				dfs(x, y);//进入第一块砖
			}

		}
	}
	res++;
	printf("%d", res);
	return 0;
}

二.P1596 [USACO10OCT] Lake Counting S 

类似上题,记录水格状态即可。

#include<iostream>
#include<stdio.h>
using namespace std;

int N, M;//N行M列
char map[105][105];//存地图
bool st[105][105];//存水的状态(是否已经联通其他已经计数的水坑)
int dx[10] = { 1,1,1,0,0,-1,-1,-1 };
int dy[10] = { 1,0,-1,1,-1,1,0,-1 };
int res;//存水坑数

//(x,y)当前判断的坐标
void dfs(int x, int y)
{
	for (int i = 0; i < 8; i++)
	{
		int a = x + dx[i], b = y + dy[i];
		if (a < 0 || a >= N || b < 0 || b >= M) continue;
		if (map[a][b] != 'W')  continue;
		if (st[a][b]) continue;

		st[a][b] = true;
		dfs(a, b);
	}
}


int main()
{
	scanf("%d %d", &N, &M);
	for (int i = 0; i < N; i++)
		scanf("%s", map[i]);
	for (int x = 0; x < N; x++)
	{
		for (int y = 0; y < M; y++)
		{
			if (map[x][y] == 'W' && st[x][y] == false)
			{
				//st[x][y] = true;//设置状态
				res++;
				dfs(x, y);
			}
		}
	}
	printf("%d", res);
	return 0;
}

 三.1114. 棋盘问题

算法思想:dfs按列扫描,在每一列具体扫描中向下扫描行,设置行的状态,当某一行已经放过棋子,跳过此行,继续扫描下一行,直到找到没有放过棋子的一行,就将此行状态设置为真。然后去扫描下一列。

ps:注意一列不放棋子的情况

const int N = 10; // n 最大是 8,多开到 10

int n, k; // n * n 棋盘,要放 k 个棋子
char g[N][N]; // 存地图
int res; // 存答案
bool row[N]; // bool 数组存每一行是否放过棋子
int cnt; // 存目前放了多少个棋子

//现在放第x列(按列扫描)
void dfs(int x)
{
    if (cnt == k) // 如果已经放完 k 个棋子
    {
        res++; // 答案加一
        return; // 不用在执行下面的操作,直接 return
    }
    if (x > n) return; // 判断边界,如果超出棋盘范围,则之前的搜索方案不合法,直接 return
    for (int i = 1; i <= n; i++) // 枚举棋子放在第几行
    {
        if (g[i][x] == '#' && !row[i]) // 如果该位置属于棋盘范围且这行之前没有放过棋子
        {
            cnt++; // 多放一个,cnt 加一
            row[i] = true; // 标记这一行,之后不能放
            dfs(x + 1); // dfs 下一列
            cnt--; // 回溯,拿掉这颗棋
            row[i] = false; // 回溯,这一行又能放了
        }
    }
    dfs(x + 1); // 还要在进行 dfs 是因为 k <= n,有可能不是每列都放了棋子,这里的 dfs 搜索这一列不放棋子的情况
}

int main()
{
    while (scanf("%d%d", &n, &k)) // 多组数据
    {
        if (n == -1 && k == -1) break; // 输入结束,跳出循环
        for (int i = 1; i <= n; i++)
        {
            getchar(); // 因为 scanf 输入会读到换行,所以要用 getchar 先把换行读完
            for (int j = 1; j <= n; j++) scanf("%c", &g[i][j]); // 输入地图不多说
        }
        res = cnt = 0; // 初始化
        memset(row, 0, sizeof row); // 初始化
        dfs(1); // 执行 dfs
        printf("%d\n", res); // 输出答案不多说
    }
    return 0;
}

四.P1025 [NOIP2001 提高组] 数的划分 

算法思想:组合型枚举,带一个start变量保证以后的不小于start即可,在带一个即时记录所用数字和变量判断剪枝。

#include<iostream>
using namespace std;

const int N = 10;
int arr[N];//存临时分组
int n,k;
int res;//存结果


//枚举到第x份,这一份至少要大于等于start,now_sum为前面的份总和
void dfs(int x,int start,int now_sum)
{
	if (now_sum>n) return;
	if (x > k)
	{
		if (now_sum == n)
		{
			res++;
		}
		return;
	}

	for (int i = start; i<n; i++)
	{
		if (n - now_sum < i) break;
		arr[x] = i;
		dfs(x + 1,i,now_sum+i);
		arr[x] = 0;//恢复现场
	}
}

int main()
{
	cin>>n>>k;
	dfs(1,1,0);
	cout << res;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值