您现在的位置是:首页 >其他 >LeetCode·每日一题·1335. 工作计划的最低难度·动态规划网站首页其他
LeetCode·每日一题·1335. 工作计划的最低难度·动态规划
简介LeetCode·每日一题·1335. 工作计划的最低难度·动态规划
作者:小迅
链接:https://leetcode.cn/problems/minimum-difficulty-of-a-job-schedule/solutions/2271898/dong-tai-gui-hua-zhu-shi-chao-ji-xiang-x-4elt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题目
示例
思路
题意 -> 将数组分为d段,求每段最大值之和的最小值
将数据分成 d 段,每一段的最小长度为 1 ,最大长度为 size - d + 当前第几段。
最简单直接的方法是,递归枚举每一段的有效数据,然后将所有段的有效数据进行组合,得出最小的和值。比如先枚举第一段可以选择的每一个数据长度,然后取当前数据长度的最大值,将剩余值给第二段,第二段进行第一段的方法,以此类推,得到所有组合,然后取最小值。
上述方法虽然可行,但是会包含大量的重复计算,比如总长度5,第一段选择了数据长度 1 、第二段选择数据长度 2 和 第一段选择了数据长度 2 、第二段选择数据长度 1,最后第三段都剩余 3,那么肯定可以记录选择过程,当遇见相同情况时直接返回。
整个递归中有大量重复递归调用(递归入参相同)。由于递归函数没有副作用,同样的入参无论计算多少次,算出来的结果都是一样的,因此可以用记忆化搜索来优化
每一个的递归其实都是一个将大问题转换为多个小问题的过程,再在每一个小问题中取最优解,再将每一个小问题的最优解进行对比、合并,从而得到大问题的最优解。
本题中,使用dp数组记录每一个小问题的最优解。
- 其中dp[i][j]表示 第i+1个段选择的数据长度为 j+1 (i <= j <= jobDifficultySize-1 - (d-i-1)),保证最少在i之前和之后每一个段有一个数据。
- 转移方程:dp[i][j] = MIN(dp[i][j], dp[i-1][k-1] + max);即当前区间的最大值 + 上一个区间的最优解 与 当前区间的当前最优解对比
- dp[0][j]为第一个段的长度为 j,则肯定只能为 jobDifficulty[0] - jobDifficulty[j] 区间的最大值
代码注释超级详细
代码
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int minDifficulty(int* jobDifficulty, int jobDifficultySize, int d){
if (jobDifficultySize < d) return -1;
int dp[d][jobDifficultySize];//定义dp数组
dp[0][0] = jobDifficulty[0];
for (int i = 1; i < jobDifficultySize-d+1; ++i) {
dp[0][i] = MAX(dp[0][i-1], jobDifficulty[i]);//初始化
}
for (int i = 1; i < d; ++i) {//枚举第一个段
for (int j = jobDifficultySize-1 - (d-i-1); j >= i; --j) {//枚举每一个有效长度
int max = INT_MIN;
dp[i][j] = INT_MAX;
for (int k = j; k >= i; --k) {//枚举有效长度的每一个位置
max = MAX(max, jobDifficulty[k]);//保存每一个位置的最小值
dp[i][j] = MIN(dp[i][j], dp[i-1][k-1] + max);//保存最优解
}
}
}
return dp[d-1][jobDifficultySize-1];
}
作者:小迅
链接:https://leetcode.cn/problems/minimum-difficulty-of-a-job-schedule/solutions/2271898/dong-tai-gui-hua-zhu-shi-chao-ji-xiang-x-4elt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。