动态规划入门详解+例题P1049装箱问题/62. Unique Paths
动态规划入门详解+例题P1049装箱问题/62. Unique Paths
小蒟蒻自己的学习记录,呜呜呜
算法思想
动态规划是一种分治思想,但与分支算法不同的是,动态规划也是把原问题分解为若干子问题,然后自底向上,求解最小的子问题,叭结果存储在表格中,再求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。
题目特点/算法要素
-
计数
有多少种方法走到右下角
有多少种方法选出k个数使得和是Sum
-
求最大最小值
从左上角走到右下角路径的最大数字和
最长上升子序列长度
-
求存在性
去石子游戏,先手是否必胜
能不能选出k个数使得和是Sum
性质:
(1)最优子结构
最优子结构性质是指问题的最优解包含其子问题的最优解。最优子结构是使用动态规划的最基本条件,如果不具有最优子结构性质,就不可以使用动态规划解决
(2)子问题重叠
子问题重叠是指在求解子问题的过程中,有大量的子问题时重复的,那么只需要求解一次,然后把结果存储在表中,以后使用时可以直接查询,不需要再次求解。子问题重叠不是使用动态规划的必要条件,但问题存在子问题重叠更能够充分彰显动态规划的优势
做题方法
一、确定状态
解动态规划的时候一般需要开一个数组(一维,二维,三维),数组的每个元素 f [i] 或者 f [i] [j] 代表什么
最后一步: 最优策略的最后一个决策
子问题: 剩下的问题
二、转移方程
三、初始条件和边界情况
初始条件: 用转移方程算不出来的,需要手动定义
边界情况: 不要数组越界,向下越负数和向上越界都不行
四、计算顺序
一维(大多数情况): 从小到大
二维: 从左到右,从上到下
因为计算后面的时候,需要用到前面的数据,前面的需要先计算出来(你要使用的状态一定要先于正在计算的状态计算出来)
例题——P1049 [NOIP2001 普及组] 装箱问题
题目描述
有一个箱子容量为V(正整数,0≤V≤20000),同时有n个物品(0<n≤30,每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入格式
11个整数,表示箱子容量
11个整数,表示有n个物品
接下来n行,分别表示这n个物品的各自体积
输出格式
11个整数,表示箱子剩余空间。
输入输出样例
输入
24
6
8
3
12
7
9
7
输出
0
说明/提示
【题目来源】
NOIP 2001 普及组第四题
Solution
一、确定状态
转换:最小剩余空间,即最大的可装重量
分别开一个200000的数组和35的数组即可
f ( m - w[i] ) 指在装了物品i后,箱子的剩余容量能装的最大重量
f ( m - w[i] ) + w[i] 指在在装了物品i后,箱子能装的最大重量
最后一步: 最优策略后剩余的最小容量
子问题: 最大重量的物品放入箱子内被装下的最大容量
二、转移方程
f (m) = max ( f ( m - w[i] ) + w[i], f (m) )(w为重量,即价值)
三、初始条件和边界情况
初始条件: 无
边界情况: 不要数组越界,向下越负数和向上越界都不行
四、计算顺序
一维(大多数情况): 从小到大
因为计算后面的时候,需要用到前面的数据,前面的需要先计算出来(你要使用的状态一定要先于正在计算的状态计算出来)
Code
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m,n,i,j;
int f[200000];
int w[35];
cin>>m>>n;
for(i=1;i<=n;++i)
cin>>w[i];
for(i=1;i<=n;++i)
{
for(j=m;j>=w[i];--j)
//由于最后要算剩余的,所以从大的开始算
{
if(f[j]<f[j-w[i]]+w[i])//判断剩余箱子容量大于0
{
f[j]=f[j-w[i]]+w[i];
}
}
}
cout<<m-f[m]<<endl;
//最后用箱子容量减去最大可装载的重量,即剩余的容量
return 0;
}
例题——62. Unique Paths
传送门:62. Unique Paths
题目描述
A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?
输入输出样例
Example 1:
Input: m = 3, n = 7
Output: 28
Example 2:
Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner,there are a total of 3 ways to
reach the bottom-right corner:
1.Right -> Down -> Down
2.Down -> Down -> Right
3.Down -> Right -> Down
Example 3:
Input: m = 7, n = 3
Output: 28
Example 4:
Input: m = 3, n = 3
Output: 6
Constraints:
1 <= m, n <= 100
It's guaranteed that the answer will be less than or equal to 2 * 109.
Solution
一、确定状态
开一个二维数组
f [i-1] [j] 表示机器人有多少种方式走到(i-1,j)
f [i] [j-1] 表示机器人有多少种方式走到(i,j-1)
最后一步: 无论机器人用何种方式到达右下角,总有最后挪动的一步——向右或者向下,设右下角的坐标为(m-1,n-1),那么前一步机器人一定是在(m-2,n-1)或者(m-1,n-2)
子问题: 机器人有多少种方式从左上角走到(m-2,n-1)和(m-1,n-2)。如果机器人有x种方式从左上角走到(m-2,n-1),有y中方式从左上角走到(m-1,n-2),那么机器人有x+y中方式走到(m-1,n-1)
二、转移方程
f [i] [j] = f [i-1] [j] + f [i] [j-1]
三、初始条件和边界情况
初始条件: f [0] [0] = 1;因为机器人只有一种方式到左上角
边界情况: i = 0 或 j = 0,则前一步只能有一个方向过来,即 f [i] [j] = 1;
四、计算顺序
二维: 从左到右,从上到下
f [0] [0] = 1;
计算第0行:
计算第1行:
.
计算第m-1行:
Code
//LeeCode交的时候,用的java
public class Solution {
public int uniquePaths(int m, int n) {
int[][] f = new int[m][n];
int i,j;
for(i = 0; i < m; ++i){
for(j = 0; j < n; ++j){
if(i == 0 || j == 0){
f[i][j] = 1;
}
else{
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
}
}
return f[m-1][n-1];
}
}
//重新写了一遍c++
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m,n,i,j;
cin>>m>>n;
int f[m][n];
for(i=0;i<m;++i)//行,上到下
{
for(j=0;j<n;++j)//列,左到右
{
if(i==0 || j==0){
f[i][j] = 1;
}
else
{
f[i][j] = f[i-1][j]+f[i][j-1];
}
}
}
cout<<f[m-1][n-1];
return 0;
}