执行结果:通过
执行用时和内存消耗如下:



#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
int maxValueOfCoins(int** piles, int pilesSize, int* pilesColSize, int k){
int dp[k + 1], tdp[k + 1];
int i, j, p, q, ans = 0;
memset(dp, 0, sizeof(int) * (k + 1));
for (i = 1; i <= pilesSize; i++) {
for (j = 1; j < pilesColSize[i - 1]; j++) {
piles[i - 1][j] += piles[i - 1][j - 1];
}
memcpy(tdp, dp, sizeof(int) * (k + 1));
for (j = 1; j <= k; j++) {
q = min(j, pilesColSize[i - 1]);
for (p = 0; p <= q; p++) {
dp[j] = max(dp[j], tdp[j - p] + (p > 0 ? piles[i - 1][p - 1] : 0));
}
}
}
return dp[k];
}
解题思路:
- 宏定义 :
max(a, b):返回a和b中的较大值。min(a, b):返回a和b中的较小值。
- 动态规划数组初始化 :
dp[k + 1]和tdp[k + 1]:dp数组用于存储当前状态下的最优解,tdp数组作为临时数组,用于存储上一个状态的最优解,以便在更新dp时不会覆盖未使用的旧值。数组大小为k + 1,因为可以选择的堆数从 0 到k。
- 预处理每堆硬币的累积和 :
- 遍历每一堆硬币
piles[i - 1](注意数组索引从 0 开始,但堆的索引从 1 开始考虑,所以这里用i - 1),将每堆硬币转化为累积和数组。即piles[i - 1][j]表示从第 1 堆到第j堆的累积硬币数量。这样做是为了方便后续直接通过索引获取任意连续堆的硬币总数。
- 遍历每一堆硬币
- 动态规划状态转移 :
- 外层循环遍历每一堆硬币。
- 使用
memcpy复制dp数组到tdp数组,以保存上一个状态的最优解。 - 内层循环遍历所有可能的堆数
j(从 1 到k)。q = min(j, pilesColSize[i - 1]):确定在当前堆数限制下,最多可以选择多少堆。- 再内层循环遍历从 0 到
q的所有可能选择堆数p。dp[j] = max(dp[j], tdp[j - p] + (p > 0 ? piles[i - 1][p - 1] : 0)):尝试更新状态。这里tdp[j - p]表示选择前i-1堆中j-p堆的最大硬币数,piles[i - 1][p - 1]表示选择当前堆的前p堆的累积硬币数(注意这里因为累积和的原因,piles[i - 1][p - 1]实际表示的是前p堆的总和,而非单独第p堆)。如果p为 0,即不选择当前堆的任何部分,则硬币数为 0。
- 返回结果 :
- 最终返回
dp[k],表示在不超过k堆的限制下,可以选择的最大硬币总数。
- 最终返回