如何将数组最大点积问题的自上而下DP转换为自下而上DP?
问题描述
给定两个正整数数组a和b,大小分别为n和m(n≥m),任务是通过在第二个数组中插入零(不得打乱元素顺序)来最大化点积。
数组a和b的点积定义为:a[0]*b[0] + a[1]*b[1] + … + a[n-1]*b[n-1]。
我已经写出了如下自上而下的DP(动态规划)解决方案,运行正常,但想不出对应的迭代(自下而上)实现方式:
public int recurse(int a[], int b[],int aInd, int bInd, int zeroCount,int dp[][]){ if(bInd>=b.length || aInd>=a.length) return 0; if(dp[aInd][bInd]!=-1) return dp[aInd][bInd]; if(zeroCount==0){ return dp[aInd][bInd]=a[aInd]*b[bInd]+recurse(a,b,aInd+1,bInd+1,0,dp); } int makeZero=recurse(a,b,aInd+1,bInd,zeroCount-1,dp); int noZero=a[aInd]*b[bInd]+recurse(a,b,aInd+1,bInd+1,zeroCount,dp); return dp[aInd][bInd]=Math.max(makeZero,noZero); }
自下而上迭代DP实现
首先明确:需要插入的零的总数是zeroCount = n - m,因为最终b要被补成n个元素才能和a计算点积。
自下而上的核心是从递归的终止条件反向填充DP表——递归是从aInd=0、bInd=0开始往前算,迭代则从a的最后一个元素、b的最后一个元素开始,逐步往回填充。
对应的迭代代码如下:
public int maxDotProduct(int[] a, int[] b) { int n = a.length; int m = b.length; int zeroCount = n - m; // dp[i][j] 表示处理a的第i个元素(a[i])及之后、b的第j个元素(b[j])及之后时,能得到的最大点积 int[][] dp = new int[n+1][m+1]; // 边界初始化:当b的所有元素都处理完(j=m),剩下的a元素只能和零相乘,贡献为0 for (int i = 0; i <= n; i++) { dp[i][m] = 0; } // 边界初始化:当a的所有元素都处理完(i=n),没有元素可计算,贡献为0 for (int j = 0; j <= m; j++) { dp[n][j] = 0; } // 从后往前遍历填充dp表 for (int i = n-1; i >= 0; i--) { for (int j = m-1; j >= 0; j--) { // 剩余可插入的零数:a还剩n-i个元素,b还剩m-j个元素,差值就是还能插的零数 int remainingZeros = (n - i) - (m - j); if (remainingZeros <= 0) { // 没零可插了,必须把当前a[i]和b[j]匹配 dp[i][j] = a[i] * b[j] + dp[i+1][j+1]; } else { // 两种选择:给a[i]插零(不使用b[j],处理下一个a元素);匹配b[j](同时处理下一个a和b元素) int chooseZero = dp[i+1][j]; int chooseMatch = a[i] * b[j] + dp[i+1][j+1]; dp[i][j] = Math.max(chooseZero, chooseMatch); } } } return dp[0][0]; }
思路说明
- DP表定义:
dp[i][j]代表处理到a[i]和b[j]时,从当前位置到数组末尾能获得的最大点积。 - 边界条件:
- 当
j == m(b的元素全部用完),剩下的a元素只能和插入的零相乘,所以这部分贡献为0。 - 当
i == n(a的元素全部处理完),没有元素可以继续计算,贡献为0。
- 当
- 状态转移:
- 计算剩余可插入的零数:用
a剩余元素数减去b剩余元素数,得到还能插入的零的数量。 - 如果没有剩余零,只能将
a[i]和b[j]相乘,再加上后续位置的最大点积。 - 如果还有零可以插,取两种选择的最大值:要么给当前
a[i]配零(跳过b[j],处理下一个a元素),要么将a[i]和b[j]匹配,同时处理下一组元素。
- 计算剩余可插入的零数:用
内容的提问来源于stack exchange,提问作者Random Dude
相关产品推荐
相关产品推荐

