A. 乳草入侵

乳草入侵题面

题目大意

有一片 n*m 大小的草地," . " 表示草," * " 表示石头,其中点(mx,my)处有一颗乳草,乳草每个星期能往他周围八个方向生长,石头上无法长草,问至少需要多少个星期乳草就能覆盖整个草地(数据保证一定能长满整个草地)。

  • 1 <= n, m <= 100
  • 时限 1s

解题思路

因为问的是至少多少个星期,所以我们考虑用 bfs ,此题为模板题,直接从起点开始 bfs ,因为数据保证了一定能覆盖完是草地的格子,所以 bfs 中最后出队的格子用的时间是最长的,直接记录最后一个出队的点的星期数就是答案,注意起点为第 0 周。

测试样例


输入
3 4 1 1
.**.
..*.
....

输出
4

AC代码

#include <bits/stdc++.h>
using namespace std;

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

char ch;
int n, m, mx, my, ans = 0;
int mp[105][105], vis[105][105];
int dx[10]={-1,-1,-1,0,0,1,1,1};
int dy[10]={-1,0,1,-1,1,-1,0,1};

queue<node> Q;

void bfs(int x, int y)
{
    Q.push({x,y,0});
    vis[x][y] = 1;
    while(!Q.empty()) {
        node f = Q.front();
        Q.pop();
        ans = f.step;
        for(int i = 0; i < 8; i++) {
            int nx = f.x + dx[i];
            int ny = f.y + dy[i];
            if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == 0 && vis[nx][ny] == 0) {
                vis[nx][ny] = 1;
                Q.push({nx, ny, f.step+1});
            }
        }
    }
}

int main()
{
    cin >> n >> m >> mx >> my;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cin >> ch;
            if(ch == '.') mp[i][j] = 0;
            else if(ch == '*') mp[i][j] = 1;
        }
    }
    bfs(mx, my);
    cout << ans << endl;
    return 0;
}

B. 分糖果

分糖果题面

题目大意

有 n 个小朋友,p 个朋友关系,现在需要发糖果,每个拿到糖果的小朋友会把多出来的部分分给还没得到糖果的朋友,但在分发的过程中,拿到糖果的小朋友会忍不住吃掉糖果,已知两个人之间需要 1 秒传递糖果,吃掉糖果需要 m 秒,问第几秒钟所有的小朋友都吃完了糖果。

  • n <= 1000
  • p <= 50000
  • 时限 1s

解题思路

朋友关系可以组成一条边,此题本质上就是从 c 号小朋友开始,按照朋友关系进行 bfs ,因为最后一个小朋友拿到糖的时间最晚,所以总时间为最后一个小朋友拿到糖果的时间 t 加上吃糖的时间 m 。此题没有直接给出图,而是给出了两点间的关系,所以可以考虑使用邻接表建无向图。

测试样例


输入
4 3 1
2
1 2
2 3
1 4

输出
5

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;

struct node
{
    int num;
    int step;
};
int n, p, c, m, ans = 0;
vector<int> E[maxn];
bool vis[maxn];
queue<node> qe;

void bfs(int id)
{
    qe.push({c,1});
    vis[c] = 1;
    while(!qe.empty()) {
        node f = qe.front();
        qe.pop();
        ans = max(ans, f.step);
        for(int i = 0; i < E[f.num].size(); i++) {
            int nxt = E[f.num][i];
            if(!vis[nxt]) {
                vis[nxt] = 1;
                qe.push({nxt, f.step+1});
            }
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &p, &c);
    scanf("%d", &m);
    for(int i = 1; i <= p; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        E[a].push_back(b);
        E[b].push_back(a);
    }
    bfs(c);
    printf("%d", ans+m);
    return 0;
}

C. 武士风度的牛

武士风度的牛

题目大意

有一头牛,走日字(与象棋的马的走法一样),从一个 n*m 地图的 K 出发,问最少需要多少步就能走到 H ,地图中 " . " 表示路, " * " 表示障碍。

  • n , m <= 150
  • 时限 1s

解题思路

bfs 模板题,注意输入的行和列是反过来的,如果没注意的话建图会出错。

测试样例


输入
10 11
..........
....*.....
..........
...*.*....
.......*..
..*..*...H
*.........
...*...*..
.K........
...*.....*
..*....*..

输出
5

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200;

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

int m, n, sx, sy, mx, my, ans = 0;
int mp[maxn][maxn], vis[maxn][maxn];
int dis[8][4] = {{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};
queue<node> Q;

void bfs(int x, int y)
{
    Q.push({x, y, 0});
    vis[x][y] = 1;
    while(!Q.empty()) {
        node f = Q.front();
        Q.pop();
        if(f.x == mx && f.y == my) {
            ans = f.step;
            break;
        }
        for(int i = 0; i < 8; i++) {
            int nx = f.x + dis[i][0];
            int ny = f.y + dis[i][5];
            if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == 0 && !vis[nx][ny]) {
                vis[nx][ny] = 1;
                Q.push({nx, ny, f.step+1});
            }
        }
    }
}

int main()
{
    cin >> m >> n;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            char ch;
            cin >> ch;
            if(ch == '*') mp[i][j] = 1;
            else mp[i][j] = 0;
            if(ch == 'K') sx = i, sy = j;
            if(ch == 'H') mx = i, my = j;
        }
    }
    bfs(sx, sy);
    cout << ans;
    return 0;
}

D. 拯救王妃

拯救王妃题面

题目大意

有一个 m*n 的矩阵,王妃被关在(a, b),王子要从(1, 1)出发,在 t 秒时限内救出王妃,王子可以往上下左右四个方向移动,矩阵每一个格子都有绑匪,消灭绑匪需要的时间都记录在矩阵中。如果王子能救出王妃,则输出 YES 和最大剩余时间,如果不能就输出 NO 。

  • 1 <= m, n <= 70
  • 题目保证答案和其他数据都在 long int 范围内
  • 时限 1s

解题思路

题目问的是最大剩余时间,所以考虑 bfs ,因为消灭绑匪的时间各有不同,所以不一定最快搜到终点就一定剩余最少时间。本题的要点在于同一个点可以重复入队(即如果能靠另一条路缩小到这个点的时间,则更新该点的最短时间),我们可以用一个 tim 数组来记录到达点 (i, j) 的时间,如果新的时间 t < tim[ i ][ j ] 则说明该点可以继续入队。因为本题只有尝试过所有的方案才能的到最优解,所以直接 bfs 整幅图就可以,不用设置终点。(本来按题目要求应该开 long long,但可能数据太水,用 int 直接莽过去了)

测试样例


输入
4 3
2 3 2
2 5 1
5 3 1
3 1 1
4 2 15

输出
YES
4

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 75;

struct node
{
    int x,y,t;
};
int m, n, a, b, t;
int mp[maxn][maxn], tim[maxn][maxn];
int dx[5] = {-1, 1, 0, 0};
int dy[5] = {0, 0, -1, 1};
queue<node> Q;
void bfs(int x, int y)
{
    Q.push({x, y, mp[x][y]});
    tim[x][y] = mp[x][y];
    while(!Q.empty()) {
        node f = Q.front();
        Q.pop();
        for(int i = 0; i < 4; i++) {
            int nx = f.x + dx[i];
            int ny = f.y + dy[i];
            int t = f.t + mp[nx][ny];
            if(nx >= 1 && nx <= m && ny >= 1 && ny <= n && t < tim[nx][ny]) {
                tim[nx][ny] = t;
                Q.push({nx, ny, t});
            }
        }
    }
}
int main()
{
    memset(tim, 0x3f, sizeof(tim));
    cin >> m >> n;
    for(int i = 1; i <= m; i++) {
        for(int j = 1; j <= n; j++) {
            cin >> mp[i][j];
        }
    }
    cin >> a >> b >> t;
    bfs(1,1);
    if(t-tim[a][b] < 0) cout << "NO";
    else cout << "YES\n" << t-tim[a][b];
    return 0;
}

E. 八数码

八数码

题目大意

有一个 3 x 3 的矩阵,有 0~8 ,其中 0 可以跟他上下左右 4 个数字进行交换,现在给你一个状态和最终状态,问最少需要多少步就可以完成转换。

  • 时限 3s

解题思路

问的是最少需要多少步,考虑用 bfs ,我们可以发现,把这个矩阵拉成 1 维后,它的每一种变化都对应着一种排列,而 9 个数一共有 9! = 362880 种排列,所以 bfs 不会超时。因为需要标记排列是否出现过,所以可以考虑使用康托展开把排列转换成一个整数表示它的状态。

测试样例


输入
2 8 3 1 6 4 7 0 5
1 2 3 8 0 4 7 6 5

输出
5

AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 11;

struct node
{
    int a[5][9];
    int x, y, step;
};

ll fact[maxn] = {1,1,2,6,24,120,720,5040,40320,362880,3628800}; // 存储1-10阶乘
int P[maxn]; // 二维转一维
int ans = 0, vis[1000005];
int dx[5] = {-1, 1, 0, 0};
int dy[5] = {0, 0, -1, 1};
ll fin;
node start, aim;
queue<node> Q;
 
ll cantor(node x, int n = 9)
{
    for(int i = 1; i <= 3; i++) {
        for(int j = 1; j <= 3; j++) {
            P[(i-1)*3+j] = x.a[i][j];
        }
    }
    ll ans = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = i+1; j <= n; j++) {
            if(P[i] > P[j]) ans += fact[n-i]; // 统计第i位后面有多少比它小的
        }
    }
    return ans;
}

// void print(node x)
// {
    // for(int i = 1; i <= 3; i++) {
        // for(int j = 1; j <= 3; j++) {
            // cout << x.a[i][j]<<" ";
        // }
        // cout << endl;
    // }
    // cout<<endl;
// }

void bfs()
{
    Q.push(start);
    vis[cantor(start)] = 1;
    while(!Q.empty()) {
        node f = Q.front(), nxt;
        Q.pop();
        if(cantor(f) == fin) {
            ans = f.step;
            break;
        }
        for(int i = 0; i < 4; i++) {
            int nx = f.x + dx[i];
            int ny = f.y + dy[i];
            if(nx >= 1 && nx <= 3 && ny >= 1 && ny <= 3) {
                nxt = f;
                swap(nxt.a[f.x][f.y], nxt.a[nx][ny]);
                nxt.x = nx, nxt.y = ny, nxt.step = f.step + 1;
                if(!vis[cantor(nxt)]) {
                    vis[cantor(nxt)] = 1;
                    Q.push(nxt);
                }
            }
        }
    }
}

int main()
{
    for(int i = 1; i <= 3; i++) {
        for(int j = 1; j <= 3; j++) {
            cin >> start.a[i][j];
            if(start.a[i][j] == 0) start.x = i, start.y = j;
        }
    }
    start.step = 0;
    for(int i = 1; i <= 3; i++) {
        for(int j = 1; j <= 3; j++) {
            cin >> aim.a[i][j];
        }
    }
    fin = cantor(aim); // 结束状态
    bfs();
    cout << ans;
    return 0;
}
最后修改:2022 年 02 月 21 日 09 : 16 PM
如果觉得我的文章对你有用,请随意赞赏