【搜索】搜刷刷题整理

包含洛谷和vjudgekuangbin题目(慢慢更新中)

洛谷

1.马的遍历

题目链接:https://www.luogu.com.cn/problem/P1443
题意:给你一个n×m的棋盘,然后给你马的起始位置(x,y),问题马到棋盘上所有点的最小步数,到达不了的就是-1。
思路:首先我们知道马能走日,把它的坐标模拟出来就可以了。

#include <bits/stdc++.h>

using namespace std;

int n, m, x, y;

queue<pair<int, int> > q;
int mp[410][401];
bool vis[410][410];
int dx[8] = {
   2,-2,2,-2,-1,1,-1,1}, dy[8] = {
   1,1,-1,-1,2,2,-2,-2};


signed main()
{
   
    cin >> n >> m >> x >> y;
    memset(mp, -1, sizeof mp);
    mp[x][y] = 0;
    vis[x][y] = 1;
    q.push({
   x, y});

    while (!q.empty())
    {
   
        int x = q.front().first, y = q.front().second;
        q.pop();
        for (int i = 0; i < 8; i++)
        {
   
            int tx = x + dx[i], ty = y + dy[i];
            if (tx > 0 && tx <= n && ty > 0 && ty <= m && vis[tx][ty] == 0)
            {
   
                vis[tx][ty] = 1;
                q.push({
   tx, ty});
                mp[tx][ty] = mp[x][y] + 1;
            }
        }
    }

    for (int i = 1; i <= n; i++)
    {
   
        for (int j = 1; j <= m; j++)
            printf("%-5d", mp[i][j]);
        cout << endl;
    }

    return 0;
}

2.好奇怪的游戏

题目链接:https://www.luogu.com.cn/problem/P1747
题意:给了你两匹马的坐标(x1,y1)(x2,y2),它们可以走日也可以走田,让你求它们到(1,1)的最小步数。
思路:同上

#include <bits/stdc++.h>

using namespace std;

int dx[12] = {
   2,2,-2,-2,-1,-1,1,1,-2,-2,2,2};
int dy[12] = {
   2,-2,2,-2,-2,2,-2,2,1,-1,1,-1};

struct Node
{
   
    int x, y, step;
};
queue<Node> q;
bool vis[25][25];
int nx, ny;

int bfs(int x, int y)
{
   
    q.push(Node{
   x, y, 0});
    vis[x][y] = 1;

    while (!q.empty())
    {
   
        Node p = q.front(); q.pop();

        for (int i = 0; i < 12; i ++)
        {
   
            int tx = p.x + dx[i], ty = p.y + dy[i];
            if (tx > 0 && ty > 0 && tx <= 50 && ty <= 50 && vis[tx][ty] == 0)
                q.push((Node){
   tx, ty, p.step + 1}), vis[tx][ty] = 1;
            if (tx == nx && ty == ny) return p.step + 1;
        }
    }
}

int main()
{
   
    cin >> nx >> ny;
    cout << bfs(1, 1) << endl;

    memset(vis, 0, sizeof vis);
    while (!q.empty()) q.pop();

    cin >> nx >> ny;
    cout << bfs(1, 1) << endl;

    return 0;
}

3.血色先锋队

题目链接:https://www.luogu.com.cn/problem/P1332
题意:地图是一个n×m的矩阵,然后有a个感染源,告诉了你a个感染源的位置,b个领主,让你求b个领主被感染的最短时间。
思路:就是找到a个感染源的位置,进行扩散,记录b个领主第一次被感染的时间即可。在搜索的时候,把这些感染源压入队列中,然后进行宽搜。

#include <bits/stdc++.h>

using namespace std;

int n, m, a, b;
bool vis[510][510];
int num[510][510];
int dx[4] = {
   -1, 1, 0, 0}, dy[4] = {
   0, 0, -1, 1};

struct Node
{
   
    int x, y;
};
queue<Node> q;

int main()
{
   
    cin >> n >> m >> a >> b;

    for (int i = 0; i < a; i++)
    {
   
        int x, y; cin >> x >> y;
        q.push(Node{
   x, y,});
        vis[x][y] = 1;
    }

    while (!q.empty())
    {
   
        Node cur = q.front(); q.pop();
        for (int i = 0; i < 4; i++)
        {
   
            int tx = cur.x + dx[i], ty = cur.y + dy[i];
            if (tx > 0 && tx <= n && ty > 0 && ty <= m && !vis[tx][ty])
            {
   
                q.push(Node{
   tx, ty});
                vis[tx][ty] = 1;
                num[tx][ty] = num[cur.x][cur.y] + 1;
            }

        }
    }

    for (int i = 0; i < b; i++)
    {
   
        int x, y; cin >> x >> y;
        cout << num[x][y] << endl;
    }

    return 0;
}

4. [USACO19JAN]Icy Perimeter S

题目链接:https://www.luogu.com.cn/problem/P5198
题意:给你一个n×n的图,如果‘#’是挨到一起的(指它的东南西北),那么认为它们是一个整体,让你求这些不同的整体的最大面和最大周长,如果面积相同,找周长最小的那个。
思路:首先我们知道,一个方格的面积是1,周长是4,如果有重叠,面积只需要累加,而两个连到一起,周长就会减少2,如果上下左右都有,那么周长就会减4,那么我们就可以针对某一个‘#‘来看,遍历它的四个方向,如果其中某一个位置不存在一个’#‘,那么周长就加一。
AC代码:

#include <bits/stdc++.h>

using namespace std;

int n;
char mp[1010][1010];
bool vis[1010][1010];
int max_s, min_c, s, c;
int dx[4] = {
   -1, 1, 0, 0}, dy[4] = {
   0, 0, -1, 1};

void dfs(int x, int y)
{
   
    if (vis[x][y]) return ;
    vis[x][y] = 1;
    s ++;

    for (int i = 0; i < 4; i++)
    {
   
        int tx = x + dx[i], ty = y + dy[i];
        if (tx < 1 || tx > n || ty < 1 || ty > n || mp[tx][ty] == '.') c ++;
        if (mp[tx][ty] == '#') dfs(tx, ty);
    }
}

int main()
{
   
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
        {
   
            if (mp[i][j] == '#' && !vis[i][j])
            {
   
                s = 0, c = 0;
                dfs(i, j);
                if (s > max_s) max_s = s, min_c = c;
                else if (s == max_s) min_c = min(min_c, c);
            }
        }

    cout << max_s << ' ' << min_c << endl;

    return 0;
}

5.单词接龙

题目链接:https://www.luogu.com.cn/problem/P1019
题意:给你n个字符串,让你找到它们能接成的最长的字符串,求其长度。每个字符串最多能使用两次,每个字符串之间是不能完全包含的。并且给出了你起始的字母。
思路:最先看到这个题的时候没得啥子思路,后面想了一下,你要连接成一个最长的字符出最先开始,你首先肯定要选两个来连接,那我们可以先把所有可以连接的两个字符串来接起来,并且记录它们的长度,并且都是第一个接到第二个后面,这跟我们后面的连接也并不冲突,所以我们用一个二维的数组连表示连接第i个字符串和第j个字符串组成的新的字符串,原字符串的长度,意思就是减去重合部分。

int cal(int x, int y)
{
   
    bool flag = true;
    int idx_y = 0;
    for (int i = str[x].size() - 1; i >= 0; i--)
    {
   
        for (int j = i; j < str[x].size(); j++)
        {
   
            if (str[x][j] != str[y][idx_y ++])
            {
   
                flag = false;
                break;
            }
        }

        if (flag == true)
            return str[x].size() - i;
        idx_y = 0;
        flag = true;
    }

    return 0;
}

经过上面的步骤,我们得到了连接任意两个字符串可以得到的长度。然后题目中给你了起始的字母,这个时候我们需要循环找到起始字母是给定的那个字母,以它开始进行dfs,在dfs中,有三种情况是不满足的,1.完全包含关系。2.连接这两个的形成的新字符串的长度为0。3.某个字符串使用的次数超过两次。后面的就是回溯dfs了。

void dfs(int x)
{
   
    bool flag = false;
    for (int i = 1; i <= n; i++)
    {
   
        if (vis[i] >= 2) continue;
        if (overlap[x][i] == 0) continue;
        if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;

        temp += str[i].size() - overlap[x][i];
        vis[i] ++;
        flag = true;
        dfs(i);
        temp -= str[i].size() - overlap[x][i];
        vis[i] --;
    }

    if (flag == false) ans = max(ans, temp);
    return ;
}

AC代码

#include <bits/stdc++.h>

using namespace std;

int n;
string str[30];
int overlap[30][30], vis[30];

char ch;
int ans = -1;
int temp = 0;

int cal(int x, int y)
{
   
    bool flag = true;
    int idx_y = 0;
    for (int i = str[x].size() - 1; i >= 0; i--)
    {
   
        for (int j = i; j < str[x].size(); j++)
        {
   
            if (str[x][j] != str[y][idx_y ++])
            {
   
                flag = false;
                break;
            }
        }

        if (flag == true)
            return str[x].size() - i;
        idx_y = 0;
        flag = true;
    }

    return 0;
}

void dfs(int x)
{
   
    bool flag = false;
    for (int i = 1; i <= n; i++)
    {
   
        if (vis[i] >= 2) continue;
        if (overlap[x][i] == 0) continue;
        if (overlap[x][i] == str[x].size() || overlap[x][i] == str[i].size()) continue;

        temp += str[i].size() - overlap[x][i];
        vis[i] ++;
        flag = true;
        dfs(i);
        temp -= str[i].size() - overlap[x][i];
        vis[i] --;
    }

    if (flag == false) ans = max(ans, temp);
    return ;
}

signed main()
{
   
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> str[i];
    cin >> ch;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            overlap[i][j] = cal(i, j);

    for (int i = 1; i <= n; i++)
    {
   
        if (str[i][0] == ch)
        {
   
            vis[i] ++;
            temp = str[i].size();
            dfs(i);
            vis[i] = 0;
        }
    }

    cout << ans << endl;

    return 0;
}

6.CF510B Fox And Two Dots

题目链接:https://www.luogu.com.cn/problem/CF510B
题意:给你一个n×m的棋盘,然后棋盘上有不同颜色的点,问你是否存在一个环,这个环的颜色相同。
思路:我们想一下,怎样才能构成一个点,首先颜色相同,其次是,我们在搜索的时候,最后一个点肯定是起点,只有满足上面两个点,才说明能够构成一个环。
那么我们在搜索的时候,对于每一个点,我们需要确定,此时位置的点的坐标和这个点是由那个点变过来的,因为我们在bfs的时候,可能会存在这样的情况,就是新访问的这个点,可能是上一个点的上一个点,显然这种情况我们应该继续搜素。所以对于每一个点和bfs我们就该这么写:

struct Node
{
   
    int x, y;
    int lx, ly;
};

void bfs(int x, int y)
{
   
    q.push({
   x, y, -1, -1});

    while (!q.empty())
    {
   
        Node cur = q.front(); q.pop();
        vis[cur.x][cur.y] = 1;

        for (int i = 0; i < 4; i++)
        {
   
            int tx = cur.x + dx[i], ty = cur.y + dy[i];
            if (tx < 1 || tx > n || ty < 1 || ty > m || (tx == cur.lx && ty == cur.ly)) continue;
            if (mp[cur.x][cur.y] != mp[tx][ty]) continue;

            if (vis[tx][ty])
            {
   
                flag = true;
                break;
            }
            else q.push({
   tx, ty, cur.x, cur.y});
        }
        if (flag) break
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值