问题描述
小M负责管理一块农田,他需要通过修建两条灌溉渠道来提升作物的总产量。农田可以表示为一个n行m列的二维数组cropField,其中cropField[i][j]代表第i行第j列的作物产量。小M的灌溉方案是:
- 一条渠道从上到下灌溉整行,另一条渠道从左到右灌溉整列。
- 被灌溉的行或列上的作物产量会加倍(即对应位置的作物产量变为原来的两倍)。
- 只能给每个作物位置加倍一次,即同一位置不能同时被两条渠道灌溉。
你需要帮小M找出哪一行和哪一列应该被灌溉,使得作物的总产量最大化。
解题思路
这是一个较为典型的可以用动态规划来解决的问题。我们首先计算出每一个行列的和以待备用,这里就要说一下了,AI刷题中无法使用numpy,只有标准库,不然能使用numpy的话这里会更简单计算。
如果可以的话,引入numpy
import numpy as np
只要执行以下语句就可以完成计算并返回结果
# 计算初始总产量
total = np.sum(cropField)
# 计算每一行的和
row_sum = np.sum(cropField, axis=1)
# 计算每一列的和
col_sum = np.sum(cropField, axis=0)
不过网页中的AI刷题并不支持安装第三方库,所以需要我们自己计算实现。
然后是关于动态规划的转移方程,对于每一个dp[i][j],我们所要的是dp[i-1][j],dp[i][j-1]和他本身 sum_row[i]+sum_col[j]-cropField[i][j]的最大值就是所求。因为对dp[i][j]来说,dp[i-1][j]是[i- 1]*[j]数组灌溉后的最大值,dp[i][j-1]是[i]*[j-1]数组灌溉后的最大值,加上他本身进行比较所求就是[i]*[j]的灌溉最大值,而对第一行和第一列需要特别计算一下,因为它们没有上一行或前一列。
以下是用实现这一方法的代码:
def solution(m: int, n: int, cropField: list) -> int:
# write code here
sum_row=[]
for i in range(n):
sum_=0
for j in range(m):
sum_+=cropField[i][j]
sum_row.append(sum_)
sum_col=[]
for j in range(m):
sum_=0
for i in range(n):
sum_+=cropField[i][j]
sum_col.append(sum_)
dp=[[0 for j in range(m)] for i in range(n)]
for i in range(n):
for j in range(m):
if i==0 and j==0:
dp[i][j]=sum_row[i]+sum_col[j]-cropField[i][j]
elif i ==0:
dp[i][j]=max(dp[i][j-1],sum_row[i]+sum_col[j]-cropField[i][j])
elif j==0:
dp[i][j]=max(dp[i-1][j],sum_row[i]+sum_col[j]-cropField[i][j])
else:
dp[i][j]=max(dp[i-1][j],dp[i][j-1],sum_row[i]+sum_col[j]-cropField[i][j])
total=0
for i in range(n):
total+=sum_row[i]
return dp[n-1][m-1]+total
复杂度分析
时间复杂度:初始化 sum_row 和 sum_col 的时间复杂度都是 O(n×m)。动态规划填充 dp 数组的时间复杂度是 O(n×m)。计算总和 total 的时间复杂度是 O(n)。
因此,整体时间复杂度是 O(n×m)
空间复杂度:使用了 dp[n][m] 和辅助数组(sum_row, sum_col),空间复杂度是 O(n * m)
总结
动态规划其实实现也不难,主要是要明确动态转移函数是什么。有了转移函数很快就可以完成解答。