C语言 一种求解两农户采苹果问题算法

pinkon5k  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(136)

我最近在解决这个问题:
在一个巨大的果园里,你发现自己置身于m行n列的苹果树中,每棵树上都有一定数量的成熟苹果,用矩阵网格表示。这个矩阵的维数为m × n,表示你的苹果田,其中grid[i][j]表示你可以从树的位置(i,j)收集到的苹果数量。
为了帮助你采摘苹果,你有两个专门的果农:

  • 果农1,驻守左上角(0,0)
  • 果农2,驻守右上角(0,n - 1)
  • 你的目标是设计一个计划,最大限度地提高总数量的苹果收集的农民

按照以下规则打印这两个勤劳的果农可以收集的苹果的最大数量:

  • 从任何单元格(i,j),农民可以移动到下一行中的以下相邻单元格之一:左下(i + 1,j - 1)、向下(i + 1,j)或右下(i + 1,j + 1)
  • 每当一个农民经过一个细胞,他们立即收集所有的苹果从这棵树,树是空的
  • 如果两个农民碰巧降落在同一个小区,他们必须分享苹果,但只有一个农民可以收集它们。
  • 农民们在任何时候都不允许冒险离开果园
  • 两个农民都必须到达果园的最下面一排才能完成摘苹果的使命

我很难理解两个农民是如何互动的。这道题我怎么解?
我想出了使用动态规划来解决每个农民的每个网格最大收获。但我不知道如何解决这个问题。
我的代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_ROWS 100
#define MAX_COLS 100

int main() {
    int m = 3;
    int n = 3;
    //example
    int grid[MAX_ROWS][MAX_COLS] = {
        {3, 1, 1},
        {5, 6, 2},
        {9, 6, 7}
    };

    int dp[MAX_ROWS][MAX_COLS] = { 0 };

    for (int j = 0; j < n; j++) {
        dp[m - 1][j] = grid[m - 1][j];
    }
    
    for (int i = m - 2; i >= 0; i--) {
        for (int j = 0; j < n; j++) {
            dp[i][j] = grid[i][j] + dp[i + 1][j];
            if (j > 0) {
                dp[i][j] = max(dp[i][j], grid[i][j] + dp[i + 1][j - 1]);
            }
            if (j < n - 1) {
                dp[i][j] = max(dp[i][j], grid[i][j] + dp[i + 1][j + 1]);
            }
        }
    }
  
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d ",dp[i][j]);
        }
        printf("\n");
    }

    return 0;
}

结果:

18 16 16
14 15 9
9 6 7
hjqgdpho

hjqgdpho1#

目前还不清楚农民必须采取相同的回合数,所以让我们假设在每次迭代中,我们有多达6个选项:农民1向下_左、下、右下移动,农民2也是如此。
当农民处于边缘或底部时,计数可以小于6。
请注意,没有循环。每个农民最终都会使MAX_ROWS-1向下移动。
解决方案涉及递归。
考虑 * 状态 *:

  • int grid[MAX_ROWS][MAX_COLS]
  • int farmer_row[2]; int farmer_col[2]
  • int move_count(初始值为0)
  • int move[(MAX_ROWS - 1) * 2]来记录6步中的哪一步。(无需初始化)
  • 苹果总数
  • 最佳苹果总数
  • 最佳移动列表。

1.初始化状态
1.调用递归function(state)
尝试每一个6移动保持跟踪,如果至少有一个是法律的。
B.对于每一步:走一步并记录下最好的一步,(最大的苹果和-递归返回值)。返回值-1表示递归未能达到法律的结束状态。
C.如果在最后一步和苹果总和比最好的苹果总和好,复制总和和移动列表到最好的。
D.每次移动:撤消移动的效果。
E.如果没有找到法律的移动返回-1,否则返回最佳和。
1.报告最好的总和和移动列表。
更高级的方法将预先计算每行最高的2棵树的总和,并在检查6种可能的移动和当前总和时使用。如果每个农民的当前和+剩余行中最好的2行小于先前移动的和,则不需要递归该移动。

i1icjdpr

i1icjdpr2#

解决这个问题的一种方法可能是同时考虑这两种立场。
Python代码:

def valid(l, r, n):
  return 0 <= l < n and 0 <= r < n and l <= r

def moves(l, r, n):
  candidates = [
    (l, r - 1),
    (l, r),
    (l, r + 1),
    (l - 1, r - 1),
    (l - 1, r),
    (l - 1, r + 1),
    (l + 1, r - 1),
    (l + 1, r),
    (l + 1, r + 1)
  ]
  return filter(lambda lr: valid(lr[0], lr[1], n), candidates)
    

def f(grid):
  m = len(grid)
  n = len(grid[0])
  dp = {
    (0, 0): grid[0][0] + grid[0][n-1]
  }
  for row in range(1, m):
    dp2 = {}
    for (l, r) in dp:
      for (ll, rr) in moves(l, r, n):
        count = grid[row][ll] + (
          grid[row][rr] if rr != ll else 0)
        new_total = dp[(l, r)] + count 
        if (ll, rr) in dp2:
          dp2[(ll, rr)] = max(
            dp2[(ll, rr)], new_total)
        else:
          dp2[(ll, rr)] = new_total
    dp = dp2
  return max(dp.values())

grid = [
  [3, 1, 1],
  [5, 6, 2],
  [9, 6, 7]
]

print(f(grid)) # 31

相关问题