动态规划入门详解+例题P1049装箱问题/62. Unique Paths

动态规划入门详解+例题P1049装箱问题/62. Unique Paths

小蒟蒻自己的学习记录,呜呜呜

算法思想

动态规划是一种分治思想,但与分支算法不同的是,动态规划也是把原问题分解为若干子问题,然后自底向上,求解最小的子问题,叭结果存储在表格中,再求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。

题目特点/算法要素

  1. 计数

    有多少种方法走到右下角

    有多少种方法选出k个数使得和是Sum

  2. 求最大最小值

    从左上角走到右下角路径的最大数字和

    最长上升子序列长度

  3. 求存在性

    去石子游戏,先手是否必胜

    能不能选出k个数使得和是Sum

性质:

(1)最优子结构

最优子结构性质是指问题的最优解包含其子问题的最优解。最优子结构是使用动态规划的最基本条件,如果不具有最优子结构性质,就不可以使用动态规划解决

(2)子问题重叠

子问题重叠是指在求解子问题的过程中,有大量的子问题时重复的,那么只需要求解一次,然后把结果存储在表中,以后使用时可以直接查询,不需要再次求解。子问题重叠不是使用动态规划的必要条件,但问题存在子问题重叠更能够充分彰显动态规划的优势

做题方法

一、确定状态

解动态规划的时候一般需要开一个数组(一维,二维,三维),数组的每个元素 f [i] 或者 f [i] [j] 代表什么

最后一步: 最优策略的最后一个决策

子问题: 剩下的问题

二、转移方程

三、初始条件和边界情况

初始条件: 用转移方程算不出来的,需要手动定义

边界情况: 不要数组越界,向下越负数和向上越界都不行

四、计算顺序

一维(大多数情况): 从小到大

二维: 从左到右,从上到下

因为计算后面的时候,需要用到前面的数据,前面的需要先计算出来(你要使用的状态一定要先于正在计算的状态计算出来)

例题——P1049 [NOIP2001 普及组] 装箱问题

传送门: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;
}

猜你喜欢

转载自blog.csdn.net/m0_51344172/article/details/113482064