SZTU 2024寒假集训 搜索(c/c++)

本文介绍了如何使用深度优先搜索(DFS)和广度优先搜索(BFS)算法解决迷宫路径数、马的移动步数、填涂颜色和棋盘问题,以及避免重复访问的马走日问题。
摘要由CSDN通过智能技术生成

第一节 搜索

A - 迷宫

题目大意:

给你一个N×M的迷宫,其中T处是障碍物,起点和终点都给你确定了,让你求从起点到终点的路径数。

思路:

初始方向数组,然后用DFS搜索即可

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <set>
#include <array>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;

int ans = 0;
int sx, sy, fx, fy;
int n, m, t;
int vis[100][100] = {0};
int dx[] = {0, 1, -1, 0, 0}; // 上下左右
int dy[] = {0, 0, 0, -1, 1};
void dfs(int x, int y)
{
    int xx, yy;
    // 如果当前坐标与目标坐标重合,则答案加1
    if (x == fx && y == fy)
    {
        ans++;
        return;
    }
    // 遍历当前坐标的四个方向
    f(i, 1, 4)
    {
        // 计算当前坐标下一个坐标
        xx = x + dx[i];
        yy = y + dy[i];
        // 如果下一个坐标未访问过,且坐标合法(在网格内)
        if (!vis[xx][yy] && xx >= 1 && xx <= n && yy >= 1 && yy <= m)
        {
            // 标记当前坐标已访问过
            vis[xx][yy] = 1;
            // 递归访问当前坐标下一个坐标
            dfs(xx, yy);
            // 回溯,取消当前坐标的访问标记
            vis[xx][yy] = 0;
        }
    }
}
int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m >> t;
    cin >> sx >> sy >> fx >> fy;
    vis[sx][sy] = 1;
    int u, v;
    f(i, 1, t)
    {
        cin >> u >> v;
        vis[u][v] = 1;
    }
    dfs(sx, sy);
    cout << ans << "\n";
    return 0;
}

B - 马的遍历

题目大意:

给出起始点,要求你计算出马到达棋盘上任意一个点最少要走几步

思路:

初始方向数组,然后用BFS搜索即可

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <array>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int n, m, x, y;
queue<array<int, 2>> q;
int cnt[550][550];
int vis[550][550] = {0};
int dx[] = {0, -1, -2, -2, -1, 1, 2, 2, 1};
int dy[] = {0, 2, 1, -1, -2, 2, 1, -1, -2};
void bfs(int x, int y)
{
    // 将起点放入队列
    q.push({x, y});
    int xx, yy;
    // 当队列不为空时,取出队列中的元素,进行BFS
    while (!q.empty())
    {
        xx = q.front()[0];
        yy = q.front()[1];
        q.pop();
        // 遍历8个方向
        f(i, 1, 8)
        {
            int t1 = xx + dx[i];
            int t2 = yy + dy[i];
            // 如果该点没有被访问过,且在边界内,则将其加入队列,并记录步数
            if (!vis[t1][t2] && t1 >= 1 && t1 <= n && t2 >= 1 && t2 <= m)
            {
                vis[t1][t2] = 1;
                q.push({t1, t2});
                cnt[t1][t2] = cnt[xx][yy] + 1;
            }
        }
    }
}
int main(void)
{
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    cin >> n >> m >> x >> y;
    memset(cnt, -1, sizeof(cnt));
    cnt[x][y] = 0;
    vis[x][y] = 1;
    bfs(x, y);
    f(i, 1, n)
    {
        f(j, 1, m)
        {
            printf("%-5d", cnt[i][j]);
        }
        printf("\n");
    }
    return 0;
}

C - 填涂颜色

题目大意:

由数字 0 组成的方阵中,有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2

思路:

不去访问圈内的0,但是如果被阻挡将无法全部访问完圈外的0,所以要加一圈外围的0,提供一条可以访问任意一个圈外0的位置的路

Code

#include <bits/stdc++.h>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int a[100][100];
int vis[100][100] = {0};
int n;
queue<pair<int, int>> q;
int dx[] = {0, 1, -1, 0, 0};
int dy[] = {0, 0, 0, 1, -1}; // 上,下,右,左
void bfs(int i, int j)
{
    q.push({i, j});
    
    while (!q.empty())
    {
        pair<int, int> tmp;
        tmp = q.front();
        q.pop();
        f(i, 0, 4)
        {
            int x = tmp.first + dx[i];
            int y = tmp.second + dy[i];
            if (vis[x][y] == 0 && x >= 0 && x <= n+1 && y >= 0 && y <= n+1)
            {
                q.push({x, y});
                vis[x][y] = 2;
            }
        }
    }
    return;
}
void solve()
{
    cin >> n;
    f(i, 1, n )
    {
        f(j, 1, n)
        {
            cin >> a[i][j];
            if (a[i][j] == 1)
            {
                vis[i][j] = 1;
            }
        }
    }
    bfs(0, 0);//加一圈边缘0,可以绕过墙
    f(i, 1, n )
    {
        f(j, 1, n )
        {
            if (vis[i][j] == 0)
            {
                cout << "2 ";
            }
            else
            {
                cout << a[i][j] << " ";
            }
        }
        cout << "\n";
    }
    return;
}
int main(void)
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}

D - 棋盘问题

题目大意:

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

思路:

用DFS,设立两个参数,一个行数,一个计数器,每一行都有放和不放的两个选择,分别搜

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <set>
#include <array>
#include <vector>
#include <string>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int n, k;
int ans;
char a[10][10];
int vis[10] = {0};
void dfs(int i, int cnt) // 行数与计数
{
    if (cnt == k)
    {
        ans++;
        return;
    }
    if (i > n - 1)
    {
        return;
    }
    f(j, 0, n - 1)
    {
    //考察是否能放
        if (!vis[j] && a[i][j] == '#')
        {
            vis[j] = 1;
            dfs(i + 1, cnt + 1); // 这行放;
            vis[j] = 0;
        }
    }
    dfs(i + 1, cnt); // 这行不放;
}
void solve()
{

    ans = 0;
    memset(vis, 0, sizeof(vis));
    f(i, 0, n - 1)
    {
        cin >> a[i];
    }
    dfs(0, 0);
    cout << ans << '\n';
}
int main(void)
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    while (1)
    {
        cin >> n >> k;
        if (n == -1 && k == -1)
        {
            break;
        }
        solve();
    }

    return 0;
}

E - 马走日

题目大意:

给定一个初始点,要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。

思路:

一边搜,一边计数即可

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <set>
#include <array>
#include <vector>
#include <string>
#include <queue>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int n, m, x, y;
int ans = 0;
int vis[100][100];
int dx[] = {0, 1, 2, 2, 1, -1, -2, -2, -1};
int dy[] = {0, -2, -1, 1, 2, 2, 1, -1, -2};
void dfs(int x, int y, int k)
{
    if (k == n * m - 1)
    {
        ans++;
        return;
    }
    int xx, yy;
    f(i, 1, 8)
    {
        xx = x + dx[i];
        yy = y + dy[i];
        if (!vis[xx][yy] && xx >= 0 && xx < n && yy >= 0 && yy < m)
        {
            vis[xx][yy] = 1;
            dfs(xx, yy, k + 1);
            vis[xx][yy] = 0;
        }
    }
    return;
}
void solve()
{
    cin >> n >> m >> x >> y;
    memset(vis, 0, sizeof(vis));
    ans = 0;
    vis[x][y]=1;//记得初始化
    dfs(x, y, 0);
    cout << ans << '\n';
    return;
}
int main(void)
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

F - 红与黑

题目大意:

有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。

思路:

和前面的区别不大,注意下多组输入即可

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <map>
#include <set>
#include <array>
#include <vector>
#include <string>
#include <queue>
#define ll long long
#define f(i, s, e) for (int i = s; i <= e; i++)
#define ff(i, s, e) for (int i = s; i >= e; i--)
#define maxn (ll)(2e5 + 5000)
#define INF 0x3f3f3f3f
using namespace std;
// set<int>::iterator it;
int n, m;
int ans = 0;
int vis[30][30];
string s[30];
queue<pair<int, int>> q;
int dx[] = {0, 1, -1, 0, 0};
int dy[] = {0, 0, 0, 1, -1};
void bfs(int x, int y)
{
	q.push({x, y});
	int xx, yy;
	while (!q.empty())
	{
		xx = q.front().first;
		yy = q.front().second;
		q.pop();
		f(i, 0, 4)
		{
			int t1 = xx + dx[i];
			int t2 = yy + dy[i];
			if (t1 < n && t1 >= 0 && t2 < m && t2 >= 0 && !vis[t1][t2] && s[t1][t2] != '#')
			{
				ans++;
				vis[t1][t2] = 1;
				q.push({t1, t2});
			}
		}
	}
	return;
}
void solve()
{
	int f1, f2;
	f(i, 0, n - 1)
	{
		cin >> s[i];
	}
	f(i, 0, n - 1)
	{
		f(j, 0, m - 1)
		{
			if (s[i][j] == '@')
			{
				f1 = i;
				f2 = j;
				goto out;
			}
		}
	}
out:
	ans = 0;
	memset(vis, 0, sizeof(vis));
	bfs(f1, f2);
	cout << ans << '\n';
	return;
}
int main(void)
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	while (1)
	{
		cin >> m >> n;
		if (n == 0 && m == 0)
		{
			break;
		}
		solve();
	}

	return 0;
}
  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值