04动态规划练习题解析

搜索练习题及参考代码

01爬楼梯

问题描述

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

输入描述

第一行输入一个整数n表示数组的长度

第二行是n个由空格分割的整数,代表数组cost

输出描述

输出一个整数代表达到楼梯顶部的最低花费

输入样例

3

10 15 20

输出样例

15

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n;
	cin>>n;
	vector<int> cost(n);
	for(int i=0;i<n;i++){
		cin>>cost[i];
	}
	
	vector<int> dp(n+1);
	dp[0] = 0;
	dp[1] = 0;
	
	for(int i=2;i<=n;i++){
		dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) ;
	}
    cout<<dp[n]<<endl; 
    return 0;
} 

02连续子数组的最大和

问题描述

输入一个整型数组arr,数组中的一个或连续多个整数组成一个子数组。

求所有子数组的和的最大值。

  • 1 <= arr.length <= 10^5
  • -10000 <= arr[i] <= 10000

输入描述

第一行输入一个整数n表示数组的长度

第二行是n个由空格分割的整数,代表数组arr

输出描述

输出一个整数代表达所有子数组的和的最大值

输入样例

9

-2 1 -3 4 -1 2 1 -5 4

输出样例

6

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n;
	cin>>n;
	vector<int> arr(n);
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}

	int dp=0, res=arr[0];
	
	for(int i=0;i<n;i++){
		dp = max(dp+arr[i], arr[i]);
		res = max(res, dp);
	}
    cout<<res<<endl;
    return 0;
} 

状态:dp[i] 表示以第 i 个数结尾的连续子数组的最大和;

状态转移函数:dp[i]=max(dp[i−1]+nums[i],nums[i])

边界条件:dp[0]=nums[0]

03掷骰子的N种方法

问题描述

这里有 n 个一样的骰子,每个骰子上都有 k 个面,分别标号为 1k

给定三个整数 n , ktarget ,返回有多少种可能的滚动骰子的方式,使正面朝上的数字之和等于 target

答案可能很大,你需要对 1 0 9 + 7 10^9 + 7 109+7 取模 。

  • 1 <= n, k <= 30
  • 1 <= target <= 1000

输入描述

第一行输入三个用空格分隔的整数 nktarget

输出描述

输出一个整数代表答案

输入样例

2 6 7

输出样例

6

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n, k, target;
	cin>>n>>k>>target;
	
	vector<vector<int>> dp(n+1, vector<int>(target+1));
	
	int m=min(k,target);
	for(int i=1;i<=m;i++){
		dp[1][i]=1;
	}
	
	for(int i=2;i<=n;i++){
		for(int j=i;j<=target;j++){
			for(int tk=1;j-tk>=0 && tk<=k;tk++){
				dp[i][j] = (dp[i][j] + dp[i-1][j-tk]) % 1000000007;
			}
		}
	}
    cout<<dp[n][target]<<endl;
    return 0;
} 

状态:dp[i][j] 代表 扔 i 个骰子和为 j

状态转移函数:dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 2] + ... + dp[i - 1][j-k]

边界条件: dp[1][k] = 1 ( 1<= k <= min(target, k) )

04戳气球

问题描述

n 个气球,编号为0 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。

求所能获得硬币的最大数量。

  • n == nums.length
  • 1 <= n <= 300
  • 0 <= nums[i] <= 100

输入描述

第一行输入一个整数n表示数组的长度

第二行是n个由空格分割的整数,代表数组nums

输出描述

输出一个整数代表答案

输入样例

2

1 5

输出样例

10

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n;
	cin>>n;
	vector<int> nums(n);
	for(int i=0;i<n;i++){
		cin>>nums[i];
	}

	vector<vector<int>> dp(n + 2, vector<int>(n + 2));
    vector<int> val(n + 2);
    
    val[0] = val[n + 1] = 1;
    for (int i = 1; i <= n; i++) {
        val[i] = nums[i - 1];
    }
    
	for (int i = n - 1; i >= 0; i--) {
        for (int j = i + 2; j <= n + 1; j++) {
            for (int k = i + 1; k < j; k++) {
                int sum = val[i] * val[k] * val[j];
                sum += dp[i][k] + dp[k][j];
                dp[i][j] = max(dp[i][j], sum);
            }
        }
    }
    
    cout<<dp[0][n + 1]<<endl;
    return 0;
} 

戳气球的操作会导致两个气球从不相邻变成相邻,使得后续操作难以处理。于是我们可以倒过来看这些操作,将全过程看作是每次添加一个气球。

状态:dp[i][j] 表示填满开区间 (i,j) 能得到的最多硬币数;

状态转移函数:dp[i][j] = max(nums[i]*nums[k]*nums[j]+dp[i][k]+dp[k][j]) ( i+1<=k<=j-1, i<j-1)

边界条件: dp[i][j] = 0 ( i>= j-1 )

05传递信息

问题描述

小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:

  1. 有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0

  2. 每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。

  3. 每轮信息必须需要传递给另一个人,且信息可重复经过同一个人

给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。

  • 2 <= n <= 10
  • 1 <= k <= 5
  • 1 <= relation.length <= 90, 且 relation[i].length == 2
  • 0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]

输入描述

第一行输入三个整数nkm分别表示玩家数,轮数和连接数

接下来m行,每行是2个由空格分割的整数,代表数组relation

输出描述

输出一个整数代表答案

输入样例

3 2 2

0 2

2 1

输出样例

0

参考代码

#include<iostream>
#include<vector>

using namespace std;

int main(){
	int n, k, m;
	cin>>n>>k>>m;
	vector<vector<int>> relation(m, vector<int>(2));
	for(int i=0;i<m;i++){
		cin>>relation[i][0]>>relation[i][1];
	}

	vector<int> dp(n);
    dp[0] = 1;
    for (int i = 0; i < k; i++) {
        vector<int> next(n);
        for (auto& edge : relation) {
            int src = edge[0], dst = edge[1];
            next[dst] += dp[src];
        }
        dp = next;
    }
    cout<<dp[n - 1]<<endl;
    return 0;
} 

06俄罗斯套娃信封

问题描述

给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。

当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。

请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。

注意:不允许旋转信封。

  • 1 <= envelopes.length <= 10^5
  • envelopes[i].length == 2
  • 1 <= wi, hi <= 10^5

输入描述

第一行输入一个整数n表示信封数

接下来n行,每行是2个由空格分割的整数,代表信封的宽度和高度

输出描述

输出一个整数代表答案

输入样例

4

5 4

6 4

6 7

2 3

输出样例

3

参考代码

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

bool cmp(vector<int> a, vector<int> b){
	return a[0] < b[0] || (a[0] == b[0] && a[1] > b[1]);
}

int main(){
	int n;
	cin>>n;
	vector<vector<int>> envelopes(n, vector<int>(2));
	for(int i=0;i<n;i++){
		cin>>envelopes[i][0]>>envelopes[i][1];
	}

	sort(envelopes.begin(), envelopes.end(), cmp);

        vector<int> f(n, 1);
        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (envelopes[j][1] < envelopes[i][1]) {
                    f[i] = max(f[i], f[j] + 1);
                }
            }
        }
        cout<<*max_element(f.begin(), f.end())<<endl;
    return 0;
} 

07编辑距离

问题描述

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符

  • 删除一个字符

  • 替换一个字符

  • 0 <= word1.length, word2.length <= 500

  • word1word2 由小写英文字母组成

输入描述

输入两行,每行一个单词。

输出描述

输出一个整数代表答案

输入样例

horse

ros

输出样例

3

参考代码

#include<iostream>
#include<string>
#include<vector>

using namespace std;

int main(){
	string word1, word2;
	cin>>word1>>word2;
	
	int n = word1.length();
    int m = word2.length();

    // 有一个字符串为空串
    if (n * m == 0) return n + m;

    // DP 数组
    vector<vector<int>> D(n + 1, vector<int>(m + 1));

    // 边界状态初始化
    for (int i = 0; i < n + 1; i++) {
        D[i][0] = i;
    }
    for (int j = 0; j < m + 1; j++) {
        D[0][j] = j;
    }

    // 计算所有 DP 值
    for (int i = 1; i < n + 1; i++) {
        for (int j = 1; j < m + 1; j++) {
            int left = D[i - 1][j] + 1;
            int down = D[i][j - 1] + 1;
            int left_down = D[i - 1][j - 1];
            if (word1[i - 1] != word2[j - 1]) left_down += 1;
            D[i][j] = min(left, min(down, left_down));

        }
    }
    cout<<D[n][m]<<endl;
    return 0;
} 
  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuelanghanbao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值