问题描述:
有n件物品和容量为m的背包 给出i件物品的重量以及价值求解让装入背包的物品重量不超过背包容量 且价值最大 。
特点:
设f[i][j]表示前 i 件物品 总重量不超过 j 的最大价值 可得出状态转移方程
代码:
for(int i=1;i<=n;i++)
for(int j=m;j1.0;j--){
if(a[i]<=j)
f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+b[i]);
else f[i][j]=f[i-1][j];
}
在一些情况下 题目的数据会很大 因此f数组不开到一定程度是没有办法ac。
设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程
for(int i=1;i<=n;i++){
for(int j=m;j1.=a[i];j--)
f[j]=max(f[j], f[j-a[i]]+b[i]);
}
问题描述:有n件物品和容量为m的背包 给出i件物品的重量以及价值 求解让装入背包的物品重量不超过背包容量 且价值最大 。
特点:题干看似与01一样 但它的特点是每个物品可以无限选用。
设f[j]表示重量不超过j公斤的最大价值 可得出状态转移方程
代码:
for(int i=1;i<=n;i++)
for(int j = a[i];j <= m;j++){
f[j] = max(f[j], f[j-a[i]]+b[i]);
}
三、多重背包问题
问题描述:
有n件物品和容量为m的背包 给出i件物品的重量以及价值 还有数量 求解让装入背包的物品重量不超过背包容量 且价值最大 。
特点 :
它与完全背包有类似点 特点是每个物品都有了一定的数量。状态转移方程为:
f[j] = max{f[j], f[j−k∗a[i]]+k∗b[i]}
代码
for(int i=1;i<=n;i++)
for(int j=m;j1.=a[i];j--)
for(int k=0;k<=c[i];k++){
if(j-k*a[i]<0)break;
f[j] = max(f[j], f[j-k*a[i]]+k*b[i]);
}
链接:https://leetcode-cn.com/problems/coin-change-2/ 力扣(LeetCode) 题目:给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。 一个字符串有许多子序列,比如字符串cabbeaf,它的子序列有c、abb、e、a、f,可以通过删除某些字符而变成回文字符串,字符串“cabbeaf”,删掉‘c’、‘e’、‘f’后剩下的子串“abba”就是回文字符串 伪代码 描述: 给定K个整数的序列{ N1, N2, …, NK },其任意连续子序列可表示为{ Ni, Ni+1, …, Nj }, 其中 1 <= i <= j <= K。最大连续子序列是所有连续子序中元素和最大的一个, 例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。 注意: 最大连续子序列和如果为负,则返回0;而本题目中的最大连续子序列和并不返回0,如果是全为负数,则返回最大的负数即可。 思路分析: 具有最优子结构,和重叠子问题, 动态规划的算法思路 最大连续子序列和只可能是以位置0~n-1中某个位置结尾。当遍历到第i个元素时,判断在它前面的连续子序列和是否大于0,如果大于0,则以位置i结尾的最大连续子序列和为元素i和前门的连续子序列和相加;否则,则以位置i结尾的最大连续子序列和为元素i。 问题 分析 解法 注意的是,当m=n时,即找到了符合条件的解 问题描述 函数推导 编程实现 问题描述 问题思路 问题描述 1.输入: 1.输出: 1.样例: 听说是最水的动态规划, 每次第一次计算出MaxSum(i,j)的值时,把该值保存起来,以后再遇到MaxSum(i.j)时直接取出之前第一次调用时已经存放的值即可,不必再次调用MaxSum函数作递归计算。 给定一个由n行数字组成的数字三角形如下图所示。试设计一个算法,计算出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。 对于给定的由n行数字组成的数字三角形,计算从三角形的顶至底的路径经过的数字和的最大值。 输入数据的第1行是数字三角形的行数n,1≤n≤100。接下来n行是数字三角形各行中的数字。所有数字在0…99之间。 出数据只有一个整数,表示计算出的最大值。 示例输出 30 Equal Subset Sum Partition,相等子集划分问题 Subset Sum,子集和问题 Minimum Subset Sum Difference,子集和的最小差问题 Count of Subset Sum,相等子集和的个数问题 Target Sum,寻找目标和的问题 Unbounded Knapsack,无限背包 Rod Cutting,切钢条问题 Coin Change,换硬币问题 Minimum Coin Change,凑齐每个数需要的最少硬币问题 Maximum Ribbon Cut,丝带的最大值切法 Fibonacci numbers,斐波那契数列问题 Staircase,爬楼梯问题 Number factors,分解因子问题 Minimum jumps to reach the end,蛙跳最小步数问题 Minimum jumps with fee,蛙跳带有代价的问题 House thief,偷房子问题 Longest Palindromic Subsequence,最长回文子序列 Longest Palindromic Substring,最长回文子字符串 Count of Palindromic Substrings,最长子字符串的个数问题 Minimum Deletions in a String to make it a Palindromic Partitioning,怎么分配字符,形成回文 Longest Common Substring,最长相同子串 Longest Common Subsequence,最长相同子序列 Minimum Deletions & Insertions to Transform a String into Longest Increasing Subsequence,最长上升子序列 Maximum Sum Increasing Subsequence,最长上升子序列和 Shortest Common Super-sequence,最短超级子序列 Minimum Deletions to Make a Sequence Sorted,最少删除变换出子序列 Longest Repeating Subsequence,最长重复子序列 Subsequence Pattern Matching,子序列匹配 Longest Bitonic Subsequence,最长字节子序列 Longest Alternating Subsequence,最长交差变换子序列 Edit Distance,编辑距离 Strings Interleaving,交织字符串 1、线性 DP 最经典双串: 经典问题: 打家劫舍系列: (打家劫舍3 是树形DP) 股票系列: 字符串匹配系列 2、区间 DP 3、背包 DP 4、树形 DP 5、状态压缩 DP 6、数位 DP 7、计数型 DP 8、递推型 DP 9、概率型 DP 10、博弈型 DP 翻转游戏 Nim游戏 井字游戏 11、记忆化搜索 作者:FennelDumplings 解动态规划的一般方法:从终点逐段向始点方向寻找最小(大)的方法。示例 1:
输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
class Solution {
public int change(int amount, int[] coins) {
int []dp=new int[amount+1];
dp[0]=1;
for(int coin:coins){
for(int i=coin;i<=amount;i++){
dp[i]=dp[i]+dp[i-coin];
}
}
return dp[amount];
}
}
最长回文子序列
//递归方法,求解最长回文子序列
int lps(char *str, int i, int j)
{
if (i == j)
return 1; //只有一个元素,回文长度为1
if (i > j) return 0; //因为只计算序列str[i....j]
//如果首尾相同
if (str[i] == str[j])
return lps(str, i + 1, j - 1) + 2;
//如果首尾不同
return max(lps(str, i, j - 1), lps(str, i + 1, j));
}
char str[] = "cabbeaf";
int n = strlen(str);
int res = lps(str, 0, n - 1);
cout << res<< endl;
//动态规划求解最长回文子序列,时间复杂度为O(n^2)
int lpsDp(char *str, int n)
{
int dp[10][10], tmp;
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; ++i) dp[i][i] = 1;
for (int i = 1; i < n; ++i)
{
tmp = 0;
//考虑所有连续的长度为i+1的子串,str[j....j+i]
for (int j = 0; j + i < n; j++)
{
//如果首尾相同
if (str[j] == str[j + i])
tmp = dp[j + 1][j + i - 1] + 2;
//如果首尾不同
else
tmp = max(dp[j + 1][j + i], dp[j][j + i - 1]);
dp[j][j + i] = tmp;
}
}
return dp[0][n - 1]; //返回字符串str[0...n-1]的最长回文子序列长度
}
最长公共子序列
##暴力递归
func LCSLength(x []byte, y []byte, m int, n int) int {
if m == 0 || n == 0 {
return 0
}
if x[m-1] == y[n-1] {
return LCSLength(x, y, m-1, n-1) + 1
}
len1 := LCSLength(x, y, m, n-1)
len2 := LCSLength(x, y, m-1, n)
if len1 > len2 {
return len1
}
return len2
}
## map法
func LCSLength(x []byte, y []byte, m int, n int, lookup map[string]int) int {
if m == 0 || n == 0 {
return 0
}
var key string = fmt.Sprintf("%s|%s", string(x[:]), string(y[:]))
// Contains?
_, ok := lookup[key]
if ! ok {
if x[m-1] == y[n-1] {
lookup[key] = LCSLength(x, y, m-1, n-1, lookup) + 1
} else {
len1 := LCSLength(x, y, m, n-1, lookup)
len2 := LCSLength(x, y, m-1, n, lookup)
if len1 > len2 {
lookup[key] = len1
} else {
lookup[key] = len2
}
}
}
return lookup[key]
}
func main() {
x := "ABCBDAB"
y := "BDCABA"
// lookup := make(map[string]int)
lookup := map[string]int{}
fmt.Printf("The length of LCS is %d\n",
LCSLength([]byte(x), []byte(y), len(x), len(y), lookup))
}
## 数组法
func LCSLength(x []byte, y []byte, m int, n int, lookup [][]int) int {
if m == 0 || n == 0 {
return 0
}
// no found in cache
if lookup[m][n] == 0 {
if x[m-1] == y[n-1] {
lookup[m][n] = LCSLength(x, y, m-1, n-1, lookup) + 1
} else {
len1 := LCSLength(x, y, m, n-1, lookup)
len2 := LCSLength(x, y, m-1, n, lookup)
if len1 > len2 {
lookup[m][n] = len1
} else {
lookup[m][n] = len2
}
}
}
return lookup[m][n]
}
func main() {
x := "ABCBDAB"
y := "BDCABA"
// init cache array
m := len(x)
n := len(y)
lookup := make([][]int, m+1)
for i := range lookup {
lookup[i] = make([]int, n+1)
}
fmt.Printf("The length of LCS is %d\n",
LCSLength([]byte(x), []byte(y), m, n, lookup))
}
#动态规划
func LCSLength(x []byte, y []byte) int {
var m, n int
m = len(x)
n = len(y)
lookup := make([][]int, m+1)
for i := range lookup {
lookup[i] = make([]int, n+1)
}
var i, j int
for i = 0; i <= m; i++ {
lookup[i][0] = 0;
}
for j =0; j <= n; j++ {
lookup[0][j] = 0;
}
for i=1; i <=m; i++ {
for j = 1; j <=n; j++ {
if x[i-1] == y[j-1] {
lookup[i][j] = lookup[i-1][j-1] + 1
} else {
len1 := lookup[i-1][j]
len2 := lookup[i][j-1]
if len1 > len2 {
lookup[i][j] = len1
} else {
lookup[i][j] = len2
}
}
}
}
return lookup[m][n]
}
最大连续子序列和-动态规划
状态转移方程: sum[i]=max(sum[i-1]+a[i],a[i])
代码:func maxSumOfSubArray(a []int) int {
// 初始化最大和为数组的第一个元素
maxSum := a[0]
n := len(a)
// 第 i 遍搜索从第 i 个元素开始往后搜索
for i := 0; i < n; i++ {
// sum用于记录从第 i 个元素到第 k 个元素的和,i <= k
sum := 0
for k := i; k < n; k++ {
sum += a[k]
if sum > maxSum {
//找到一个比之前找到的最大值更大的连续子序列和
maxSum = sum
}
}
}
return maxSum
}
func MaxSubArray(arr []int) int {
currSum := 0
maxSum := arr[0]
for _, v := range arr {
if currSum > 0 {
currSum += v
} else {
currSum = v
}
if maxSum < currSum {
maxSum = currSum
}
}
return maxSum
}
#py
class Solution(object):
def maxSubArray(self, nums):
for i in range(1, len(nums)):
nums[i]= nums[i] + max(nums[i-1], 0)
return max(nums)
物品称重获得最大价值,最小体积
当不选择n是,就用剩下的n-1填满m;= 1; ++i) {
if (dp[n][i] <= V) {
cout << i << endl;
break;
}
}
}
爬楼梯问题
1.假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
function fun($n) {
if($n<=0) {
return 0;
} else if($n<=2) {
return $n;
} else {
return fun($n-1)+fun($n-2);
}
}
function fun($n) {
if($n <= 0) {
return 0;
}
$temp = [1,2];
for($i=2;$i<$n;$i++) {
$temp[$i] = $temp[$i-1]+$temp[$i-2];
}
return $temp[$n - 1];
}
路径问题
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
//方法1
int uniquePaths(int m, int n) {
vector
求证数数字和
整数n和m
求和等于m的所有组合的个数。
输入:
6 8
输出
4#include
数字三角形
大体思路
这样每个MaxSum(i,j)都只需要计算一次,计算次数为数字三角形中的数字总数。因此,不需要写递归函数,从第N-1行开始向上逐行递推,就可以求得a[1][1]的值。题目描述
输入
输出
示例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
#include
经典问题
一. 0/1 Knapsack,0/1背包问题
二. Unbounded Knapsack,无限背包,5个题
三. Fibonacci Numbers,斐波那契数列,6个题
四. Palindromic Subsequence,回文子系列,5个题
Palindrome,怎么删掉最少字符构成回文五. Longest Common Substring,最长子字符串系列,13个题
another,字符串变换
最经典单串:
300. 最长上升子序列
1143. 最长公共子序列
120. 三角形最小路径和
53. 最大子序和
152. 乘积最大子数组
887. 鸡蛋掉落(DP+二分)
354. 俄罗斯套娃信封问题
198. 打家劫舍
213. 打家劫舍 II
121. 买卖股票的最佳时机
122. 买卖股票的最佳时机 II
123. 买卖股票的最佳时机 III
188. 买卖股票的最佳时机 IV
309. 最佳买卖股票时机含冷冻期
714. 买卖股票的最佳时机含手续费
72. 编辑距离
44. 通配符匹配
10. 正则表达式匹配
516. 最长回文子序列
730. 统计不同回文子字符串
1039. 多边形三角剖分的最低得分
664. 奇怪的打印机
312. 戳气球
416. 分割等和子集 (01背包-要求恰好取到背包容量)
494. 目标和 (01背包-求方案数)
322. 零钱兑换 (完全背包)
518. 零钱兑换 II (完全背包-求方案数)
474. 一和零 (二维费用背包)
124. 二叉树中的最大路径和
1245. 树的直径 (邻接表上的树形DP)
543. 二叉树的直径
333. 最大 BST 子树
337. 打家劫舍 III
464. 我能赢吗
526. 优美的排列
935. 骑士拨号器
1349. 参加考试的最大学生数
233. 数字 1 的个数
902. 最大为 N 的数字组合
1015. 可被 K 整除的最小整数
计数型DP都可以以组合数学的方法写出组合数,然后dp求组合数
62. 不同路径
63. 不同路径 II
96. 不同的二叉搜索树 (卡特兰数)
1259. 不相交的握手 (卢卡斯定理求大组合数模质数)
所有线性递推关系都可以用矩阵快速幂做,可以O(logN),最典型是斐波那契数列
70. 爬楼梯
509. 斐波那契数
935. 骑士拨号器
957. N 天后的牢房
1137. 第 N 个泰波那契数
求概率,求数学期望
808. 分汤
837. 新21点
策梅洛定理,SG 定理,minimax
293. 翻转游戏
294. 翻转游戏 II
292. Nim 游戏
石子游戏
877. 石子游戏
1140. 石子游戏 II
348. 判定井字棋胜负
794. 有效的井字游戏
1275. 找出井字棋的获胜者
本质是 dfs + 记忆化,用在状态的转移方向不确定的情况
329. 矩阵中的最长递增路径
576. 出界的路径数
链接:https://leetcode-cn.com/circle/article/NfHhXD/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。总结