想到了用动态规划的思想解决问题,但由于计算了很多无关状态,一直卡在最后一个测试样例(TLE),AC不了。
class Solution {
private int[][][] dp;
private int minPre(int m, int left, int right){
int outPut = Integer.MAX_VALUE;
// for(int i = 0; i <= m - 1; i++){
// for(int j = left + i; j < right - (m - 1 - i); j++){
// outPut = Math.min(outPut, Math.max(dp[i][left][j], dp[m - 1 - i][j + 1][right]));
// }
// }
for(int i = left + m - 1; i < right; i++){
int tmp = Math.max(dp[m - 1][left][i], dp[0][i + 1][right]);
if(tmp > outPut)
break;
else outPut = tmp;
}
return outPut;
}
public int splitArray2(int[] nums, int m) {
dp = new int[m][nums.length][nums.length];
for(int i = 0; i < nums.length; i++){
dp[0][i][i] = nums[i];
for(int j = i + 1; j < nums.length; j++){
dp[0][i][j] = dp[0][i][j - 1] + nums[j];
}
}
if(m == 1) return dp[m - 1][0][nums.length - 1];
for(int i = 1; i < m - 1; i++){
for(int j = 0; j < nums.length - i; j++){
for(int k = j + i; k < nums.length; k++){
dp[i][j][k] = minPre(i, j, k);
}
}
}
return minPre(m - 1, 0, nums.length - 1);
}
}
如minPre方法内注释掉的部分,这部分代码在计算如下问题,将8个数划分为4份时:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
7 | 10 | 3 | 5 | 4 | 7 | 1 | 9 |
会计算【(0,4)拆分成2份、(5,7)拆分成2份】的情况和【(0、5)拆3份、(6,7)拆1份】以及
【(0,6)拆3份、(7,7)拆1份】的情况。
殊不知,【(0,4)拆分成2份、(5,7)拆分成2份】中包含的所有情况都会在【(0、5)拆3份、(6,7)拆1份】和【(0,6)拆3份、(7,7)拆1份】中重复出现,所以这一部分是冗余计算。
修改后的代码仍有问题,仍存在重复计算,如下是大佬的可以AC的代码:
public int splitArray2(int[] nums, int m)
{
int L = nums.length;
int[] S = new int[L+1];
S[0]=0;
for(int i=0; i<L; i++)
S[i+1] = S[i]+nums[i];
int[] dp = new int[L];
for(int i=0; i<L; i++)
dp[i] = S[L]-S[i];
for(int s=1; s<m; s++)
{
for(int i=0; i<L-s; i++)
{
dp[i]=Integer.MAX_VALUE;
for(int j=i+1; j<=L-s; j++)
{
int t = Math.max(dp[j], S[j]-S[i]);
if(t<=dp[i])
dp[i]=t;
else
break;
}
}
}
return dp[0];
}
对比如下两段:
//我的
for(int i = 1; i < m - 1; i++){
for(int j = 0; j < nums.length - i; j++){
for(int k = j + i; k < nums.length; k++){//保存了分m-1段时,从i到j的分段情况
dp[i][j][k] = minPre(i, j, k);
}
}
}
private int minPre(int m, int left, int right){
int outPut = Integer.MAX_VALUE;
for(int i = left + m - 1; i < right; i++){
int tmp = Math.max(dp[m - 1][left][i], dp[0][i + 1][right]);
if(tmp > outPut)
break;
else outPut = tmp;
}
return outPut;
}
//大佬的
for(int s=1; s<m; s++)
{
for(int i=0; i<L-s; i++)//dp仅保存分m-1段时,从i到数组结尾的分段情况
{
dp[i]=Integer.MAX_VALUE;
for(int j=i+1; j<=L-s; j++)
{
int t = Math.max(dp[j], S[j]-S[i]);
if(t<=dp[i])
dp[i]=t;
else
break;
}
}
}
注意注释部分,我的代码保存了冗余状态,i到j的分段情况是无效数据,无需保存。