You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 418. 花生采摘

一、题目描述

鲁宾逊先生有一只宠物猴,名叫多多。这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生! ——熊字”。

鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。

在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图 1)。

有经验的多多一眼就能看出,每棵花生植株下的花生有多少。

为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找 出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”

我们假定多多在每个单位时间内,可以做下列四件事情中的一件:

  • 从路边跳到最靠近路边(即第一行)的某棵花生植株;
  • 从一棵植株跳到前后左右与之相邻的另一棵植株;
  • 采摘一棵植株下的花生;
  • 从最靠近路边(即第一行)的某棵花生植株跳回路边。

现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?

注意 可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。

例如在图 2 所示的花生田里,只有位于 (2,5),(3,7),(4,2),(5,4)的植株下长有花生,个数分别为 13,7,15,9

沿着图示的路线,多多在 21 个单位时间内,最多可以采到 37 个花生。

输入格式 输入文件的第一行包括三个整数,M,NK,用空格隔开;表示花生田的大小为 M×N,多多采花生的限定时间为 K 个单位时间。

接下来的 M 行,每行包括 N 个非负整数,也用空格隔开;第 i+1 行的第 j 个整数 P_{i,j} 表示花生田里植株 (i,j) 下花生的数目,0 表示该植株下没有花生。

输出格式 输出文件包括一行,这一行只包含一个整数,即在限定时间内,多多最多可以采到花生的个数。

数据范围 1M,N20,0K1000,0P_{i,j}500

(模拟) O(M^2N^2)

二、题目解析

由于题目中描述:

“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”

并且:

可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。

因此 整个采摘花生的过程是确定的,没有选择的余地,所以这道题目是一道模拟题,而不是最优化问题

算法流程

从初始状态开始,每次判断“走到当前最大值的位置,采摘花生,再回到马路上”整个流程的时间是否够用: 如果够用,则采摘下一最大值; 如果不够用,则停止; 有两点需要注意:

采摘花生也需要单位1的时间;

最后只需退回马路上即可,不需要返回起点,退回马路所需的时间即为当前位置的行号; 由于本题数据范围很小,所以求当前最大值时可以直接暴力枚举所有方格;

时间复杂度分析

最坏情况下会走遍 NM 个方格,每次移动时会暴力枚举求出当前最大值,求最大值操作的计算量是 O(NM),因此总时间复杂度是 O(N^2M^2)=20^4=1.6×10^5

三、实现代码

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
#define x first
#define y second

const int N = 30;

int n, m, k;
int g[N][N];

PII get_max() {
    PII r;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (g[r.x][r.y] < g[i][j])
                r = {i, j};
    return r;
}

int main() {
    cin >> n >> m >> k; // 花生田的大小n*m,k为限定的时间
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> g[i][j];

    // 首轮次第一个位置
    PII t = get_max();
    if (t.x * 2 + 1 > k) { // 从界外进入花生田,直线距离到了回不去了,表示最大的取不到啊~,之所以加1是因为摘花生还需要一个单位的时间
        puts("0");
        exit(0);
    }

    // 先把最大值拿走,之所以第一个与后续的不同,是因为第一个可以从界外任意一列进入,而其它的是从(x1,y1)某个点到(x2,y2)的,有区别,需单独处理
    int res = g[t.x][t.y];
    k -= t.x + 1;
    g[t.x][t.y] = 0;

    while (true) {
        PII r = get_max();
        if (g[r.x][r.y] == 0) break;             // 如果没有花生存在,就退出循环
        int d = abs(r.x - t.x) + abs(r.y - t.y); // 从当前位置,走到下一个最大值位置,需要走的步数
        if (d + r.x + 1 > k) break;              // 如果去摘了下一个最大值的花生,那么时间上是不是允许呢?如果不行就停止
        res += g[r.x][r.y];                      // 摘到这个花生
        g[r.x][r.y] = 0;                         // 将此位置上的花生数量清零
        k -= d + 1;                              // 时间上减去本轮的移动时间和摘花生的时间
        t = r;                                   // 更新当前位置
    }
    // 输出结果
    printf("%d\n", res);
    return 0;
}