0
点赞
收藏
分享

微信扫一扫

【搜索】搜刷刷题整理

幸福的无所谓 2022-04-02 阅读 48
c++算法

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

kuangbin

7.Fire Game(FZU - 2150)

题目链接:https://vjudge.net/problem/FZU-2150
题意:告诉你有两个人要去点火堆,意思就是选择两个点来点,然后每个火堆可以蔓延到它的上下左右四个位置,问你是否能够将所有的草堆都点燃完,如果能,输出最小的步数,不行就输出‘-1’。
思路:题目中说明了两个人去点,相当于是只能点两次,这个位置可以是相同的位置,也可以是不同的位置,那么说明连通的草堆最多就只有两堆,如果多了就会有草堆不能被点。那么我们可以知道这是一个双端的bfs,从两个方向开始找,那么就是四层循环。那么我们首先要找到连通块的数量。这里我们需要一个belong数组来保存(i,j)这个位置的草堆是属于哪一个连通块,因为后面我们在循环起点的时候需要判断两个起点是属于哪一个连通块。(下面会解释)

void find_num(int x, int y)
{
    queue<node> q;
    q.push({x, y, 0});
    block_num ++;

    while (!q.empty())
    {
        node now = q.front(); q.pop();
        belong[now.x][now.y] = block_num;

        for (int i = 0; i < 4; i++)
        {
            node cur;
            cur.x = now.x + dx[i], cur.y = now.y + dy[i], cur.step = now.step + 1;

            if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !belong[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
            {
                belong[cur.x][cur.y] = block_num;
                q.push(cur);
            }
        }
    }
}

好,加入我们确定了连通块的数量是<=2的,那么我们接下来就需要遍历两个起点了,这里我们用四层循环来找,第一个起点是属于第一个连通块的,然后第二个连通块就=block_num,这里的block_num要么等于1要么等于2,都是满足条件的,我们在每次找到的时候在答案里面去一个min就可以找到最小值。

if (block_num > 2) ans = -1;
        else
        {
            node s1, s2;

            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                    if (belong[i][j] == 1)
                    {
                        s1.step = 0, s1.x = i, s1.y = j;

                        for (int k = 1; k <= n; k ++)
                            for (int l = 1; l <= m; l++)
                                if (belong[k][l] == block_num)
                                {
                                    s2.step = 0, s2.x = k, s2.y = l;
                                    ans = min(ans, double_bfs(s1, s2));
                                }
                    }
        }

那么我们剩下的就只需要bfs一下起点就可以了,我们在这里要用一个结构体来存每一个点的坐标以及到达这个位置所需要的步数,并且这里的步数一定是最小的步数。

struct node
{
    int x, y;
    int step;
};

接下来就是bfs起点了,我们要找的是最小的时间,也就是最小的步数,在bfs里面我们需要一个变量cost来记录需要的最大步数。

int double_bfs(node s1, node s2)
{
    memset(vis, 0, sizeof vis);
    int cost = 0;
    queue<node> q;

    q.push(s1), q.push(s2);
    vis[s1.x][s1.y] = 1, vis[s2.x][s2.y] = 1;

    while (!q.empty())
    {
        node tmp = q.front(); q.pop();
        cost = tmp.step;

        for (int i = 0; i < 4; i++)
        {
            node cur;
            cur.x = tmp.x + dx[i], cur.y = tmp.y + dy[i], cur.step = tmp.step + 1;

            if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !vis[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
            {
                vis[cur.x][cur.y] = 1;
                q.push(cur);
            }
        }
    }

    return cost;
}

AC代码

#include <bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;

struct node
{
    int x, y;
    int step;
};

int ans, cnt;
int n, m;
char mp[15][15];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int block_num = 0;
int belong[15][15];
bool vis[15][15];

void find_num(int x, int y)
{
    queue<node> q;
    q.push({x, y, 0});
    block_num ++;

    while (!q.empty())
    {
        node now = q.front(); q.pop();
        belong[now.x][now.y] = block_num;

        for (int i = 0; i < 4; i++)
        {
            node cur;
            cur.x = now.x + dx[i], cur.y = now.y + dy[i], cur.step = now.step + 1;

            if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !belong[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
            {
                belong[cur.x][cur.y] = block_num;
                q.push(cur);
            }
        }
    }
}

int double_bfs(node s1, node s2)
{
    memset(vis, 0, sizeof vis);
    int cost = 0;
    queue<node> q;

    q.push(s1), q.push(s2);
    vis[s1.x][s1.y] = 1, vis[s2.x][s2.y] = 1;

    while (!q.empty())
    {
        node tmp = q.front(); q.pop();
        cost = tmp.step;

        for (int i = 0; i < 4; i++)
        {
            node cur;
            cur.x = tmp.x + dx[i], cur.y = tmp.y + dy[i], cur.step = tmp.step + 1;

            if (cur.x >= 1 && cur.x <= n && cur.y >= 1 && cur.y <= m && !vis[cur.x][cur.y] && mp[cur.x][cur.y] == '#')
            {
                vis[cur.x][cur.y] = 1;
                q.push(cur);
            }
        }
    }

    return cost;
}

int main()
{
    int t; cin >> t;
    for (int kk = 1; kk <= t; kk++) { ans = inf; block_num = 0; memset(belong, 0, sizeof belong); cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> (mp[i] + 1);

        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (!belong[i][j] && mp[i][j] == '#') find_num(i, j);

        if (block_num > 2) ans = -1;
        else
        {
            node s1, s2;

            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= m; j++)
                    if (belong[i][j] == 1)
                    {
                        s1.step = 0, s1.x = i, s1.y = j;

                        for (int k = 1; k <= n; k ++)
                            for (int l = 1; l <= m; l++)
                                if (belong[k][l] == block_num)
                                {
                                    s2.step = 0, s2.x = k, s2.y = l;
                                    ans = min(ans, double_bfs(s1, s2));
                                }
                    }
        }

        cout << "Case " << kk << ": " << ans << endl;
    }

    return 0;
}

8.Pots (POJ - 3414)

题目链接:https://vjudge.csgrandeur.cn/problem/POJ-3414
题意:现在有两个罐子,他们的大小分别为a升和b升,现在有三种操作,1.将罐子里面的水灌满。2.将罐子里的水倒空。3.将一个罐子里的水到入另一个罐子中。现在给你一个数字k,问经过怎样的操作,能最快的让其中一个罐子中水的体积到达k,输出最小的步数和操作的过程。
思路:

9.Fire! (UVA - 11624)

题目链接:https://vjudge.csgrandeur.cn/problem/UVA-11624
题意:给你一个n*m的二位字符数组(迷宫),其中‘F’表示火堆,‘#’表示墙,‘J’表示当前位置,‘.’表示可以走的点,其中火堆可以蔓延,可以到达除了火堆已经到达过的其他任何地方,而人只能到达‘.’的位置,然后问是否能在火烧到他之前到达迷宫的边缘。
思路:首先这里说了火堆能够蔓延,并且是能够到达火堆没到达过的其他任何地方,那么’.'和‘#’火堆都可以到达,那么我们先将火堆蔓延到其他每个的点的最短是时间记录下来,然后在比较人到达其他点的时间,与火堆到达的时间做比较,如果时间小于火堆到达的时间,说明这个点是可到达的。
这里相当于搜索了两遍,第一遍搜索是搜索的火堆到达其他点的时间,第二个搜索搜的时人到达其他点的时间。
这里有一个坑,题目中没有说明只有一个火堆,所以一个迷宫中可能含有多个火堆。

int fire[N][N];///用一个二位数组来表示火蔓延到其他点的时间,
///最开始fire数组要定义为inf,

///用一个结构体来表示到达每一个点的位置
struct node
{
	int x, y;
	int step;
}

寻找火堆可到达的每个点的最短时间

void fire_bfs()
{
    memset(vis, 0, sizeof vis);
    queue<node> q;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (mp[i][j] == 'F')
            {
                vis[i][j] = 1;
                fire[i][j] = 0;
                q.push({i, j, 0});
            }

    int x, y, step;
    while (!q.empty())
    {
        node tmp = q.front(); q.pop();

        for (int i = 0; i < 4; i++)
        {
            x = tmp.x + dx[i], y = tmp.y + dy[i], step = tmp.step + 1;
            if (x < 1 || x > n || y < 1 || y > m || mp[x][y] == '#' || mp[x][y] == 'F' || vis[x][y])
                continue;
            vis[x][y] = 1;
            fire[x][y] = step;
            q.push({x, y, step});
        }
    }
}

寻找最短的步数

int bfs(int x, int y)
{
    memset(vis, 0, sizeof vis);
    queue<node> q;
    q.push({x, y, 0});
    vis[x][y] = 1;

    while (!q.empty())
    {
        node now = q.front(); q.pop();
        for (int i = 0; i < 4; i++)
        {
            node tmp;
            tmp.x = now.x + dx[i], tmp.y = now.y + dy[i], tmp.step = now.step + 1;

            if (tmp.x < 1 || tmp.x > n || tmp.y < 1 || tmp.y > m) return tmp.step;

            if (fire[tmp.x][tmp.y] <= tmp.step || mp[tmp.x][tmp.y] == '#' || mp[tmp.x][tmp.y] == 'F' || vis[tmp.x][tmp.y])
                continue;
            vis[tmp.x][tmp.y] = 1;
            q.push(tmp);
        }
    }

    return 0;
}

AC代码

#include <bits/stdc++.h>

using namespace std;

struct node
{
    int x, y, step;
};

const int inf = 0x3f3f3f3f;
const int N = 1010;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};

int n, m;
char mp[N][N];
bool vis[N][N];
int fire[N][N];

void fire_bfs()
{
    memset(vis, 0, sizeof vis);
    queue<node> q;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (mp[i][j] == 'F')
            {
                vis[i][j] = 1;
                fire[i][j] = 0;
                q.push({i, j, 0});
            }

    int x, y, step;
    while (!q.empty())
    {
        node tmp = q.front(); q.pop();

        for (int i = 0; i < 4; i++)
        {
            x = tmp.x + dx[i], y = tmp.y + dy[i], step = tmp.step + 1;
            if (x < 1 || x > n || y < 1 || y > m || mp[x][y] == '#' || mp[x][y] == 'F' || vis[x][y])
                continue;
            vis[x][y] = 1;
            fire[x][y] = step;
            q.push({x, y, step});
        }
    }
}


int bfs(int x, int y)
{
    memset(vis, 0, sizeof vis);
    queue<node> q;
    q.push({x, y, 0});
    vis[x][y] = 1;

    while (!q.empty())
    {
        node now = q.front(); q.pop();
        for (int i = 0; i < 4; i++)
        {
            node tmp;
            tmp.x = now.x + dx[i], tmp.y = now.y + dy[i], tmp.step = now.step + 1;

            if (tmp.x < 1 || tmp.x > n || tmp.y < 1 || tmp.y > m) return tmp.step;

            if (fire[tmp.x][tmp.y] <= tmp.step || mp[tmp.x][tmp.y] == '#' || mp[tmp.x][tmp.y] == 'F' || vis[tmp.x][tmp.y])
                continue;
            vis[tmp.x][tmp.y] = 1;
            q.push(tmp);
        }
    }

    return 0;
}

int main()
{
    int t;
    while (cin >> t)
    {
        while (t --)
        {
            memset(mp, 0, sizeof mp);
            memset(vis, 0, sizeof vis);

            int sx, sy;
            cin >> n >> m;
            getchar();
            for (int i = 1; i <= n; i++)
            {
                cin >> (mp[i] + 1);
                for (int j = 1; j <= m; j++)
                {
                    fire[i][j] = inf;
                    if (mp[i][j] == 'J') sx = i, sy = j;
                }
            }

            fire_bfs();
            int cnt = bfs(sx, sy);
            if (cnt) cout << cnt << endl;
            else puts("IMPOSSIBLE");
        }
    }

    return 0;
}

10.迷宫问题

题目链接:https://vjudge.net/problem/POJ-3984
题意:给你一个5*5的二维数组,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
思路:首先我们可以确定,起点和终点一定是0,并且一定是可到达的,那么我们可以直接从起点开始搜索,题目中要求找到最短路线并且将最短的路线输出出来,那么我们在搜索的过程中只需要将每个点是由那个点变换过来即可。现在的问题是我们怎样记录每个点是由那个点变换过来的即可,这里我们可以使用一个二维的结构来来表示,结构体里面装的是某个点的坐标,然后结构体的下标就表示的某个点。

struct node
{
	int x, y;
}step[N][N];

那么比如step [ i ] [ j ] 就表示(i, j)这个点是由那个点变换过来的。然后后面就是正常的宽搜了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;

struct node
{
    int x, y;
}step[10][10];

int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};

int mp[10][10];
bool vis[10][10];

void print(node p)
{
    if (p.x == 0 && p.y == 0){ printf("(0, 0)\n"); return ;}///特别判断输出起点的位置,这里相当于是终点(寻找路径的终点,在什么位置停止)
    print(step[p.x][p.y]);///运用递归来输出路劲,一直寻找当前位置的前一个位置
    printf("(%d, %d)\n", p.x, p.y);
}

void bfs()
{
    node a;
    a.x = 0, a.y = 0;
    vis[0][0] = 1;
    queue<node> q;
    q.push(a);

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

        if (a.x == 4 && a.y == 4) {print(a); break ;}

        for (int i = 0; i < 4; i ++)
        {
            node tmp;
            tmp.x = a.x + dx[i], tmp.y = a.y + dy[i];

            if (tmp.x >= 0 && tmp.x < 5 && tmp.y >= 0 && tmp.y < 5 && !vis[tmp.x][tmp.y] && mp[tmp.x][tmp.y] == 0)
            {
                vis[tmp.x][tmp.y] = 1;
                step[tmp.x][tmp.y] = a;///保存当前点是由哪一个点又由来的
                q.push(tmp);
            }
        }

    }
}

int main()
{
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++) cin >> mp[i][j];

    memset(vis, 0, sizeof 0);
    bfs();

    return 0;
}
举报

相关推荐

0 条评论