OJ算法--动态规划

OJ算法–动态规划

算法概述

动态规划用于解决一定规模下的最优解问题,通过小规模最优解推导出大规模最优解,再由大规模最优解推导出更大规模最优解。以此类推最终得到问题答案,01背包问题就是最典型的动态规划问题。

例题

在刷牛客OJ时会遇到这样一道题:
在这里插入图片描述

算法原理

对于这样一个问题,影响问题规模的因素有两个,一个是需要凑出的邮票总价值,另一个是可用的邮票规模。当邮票总价值变小,或者可用邮票规模变小,亦或是两者同时变小时,问题都会变得更加简单。因此我们可以找出小规模问题和大规模问题中的某种递推公式,从而解决该题目。

因为影响问题规模的因素有两个,所以我们可以用一个二维数组来表示这个问题。如下图所示,j为邮票总价值,i为可用邮票,对于题目所给实例,可列画如下二维数组ans,此时ans[i][j]则表示在仅使用第i行及第i行以上邮票,凑出总价值为j的最少邮票数。不难看出除最后一行最后一列的ans外,任何一个ans[i][j]都是原问题的一个小规模版本,而最后一行最后一列的ans则是问题的最终答案。
在这里插入图片描述

这里第一列初始化为0是因为邮票总价值为0时,只需0张邮票就可满足条件。第一行除ans[0][0]外,初始化为99,是因为当无解时我们用99表示,这样在以后通过选取最小值的方式得到最优解的过程中,99可以被自动舍弃,这点后面会再提到。

之后我们要从左至右,从上至下依次计算出ans[i][j]。这也是从小问题向大问题不断靠近的过程。对于每一个ans[i][j]我们考虑这样一件事:此时的最优解无外乎两种情况,要么包含第i张邮票;要么不包含第i张邮票。如果不包含第i张邮票,也就是说仅使用前i-1张邮票即可得到总价值为j的最少拼凑方法,即ans[i][j] = ans[i-1][j];如果包含第i张邮票,则说明总价值j中包含第i张邮票的价值,那么抛去第i张邮票价值后的最优解再加上第i张邮票就是当前最优解,即ans[i][j] = ans[i - 1][j - val[i]] + 1。于是我们分别计算两种方法的ans[i][j]并取最小值即可。用此方法以此填满整个表格,最终最后一个值就是该问题的解。
在这里插入图片描述

可以看出,要求用1,3,3,3,4这5张邮票凑出10的面值,最少需要3张。

核心代码

#include <iostream>
#include <algorithm>
using namespace std;

int main(){
    int value;
    cin >> value;
    int n;
    cin >> n;
    //记录邮票面值
    int YouPiao[n];
    for(int i = 0; i < n; ++i)
        cin >> YouPiao[i];
    //创建二维数组
    int dp[n + 1][value + 1];
    //初始化二维数组,未计算过的dp[i][j]赋值-1,方便调试
    for(int i = 0; i < n + 1; ++i){
        for(int j = 0; j < value + 1; ++j){
            if(j == 0)   dp[i][j] = 0;
            else if(i == 0)   dp[i][j] = 99;
            else dp[i][j] = -1;
        }
    }
    //算法核心:地推的方式计算整个二维数组的值
    for(int i = 1; i < n + 1; ++i){
        for(int j = 1; j < value + 1; ++j){
            if(YouPiao[i - 1] <= j){
                dp[i][j] = min( dp[i - 1][j] , dp[i - 1][j - YouPiao[ i - 1]] + 1 );
            }
            else{
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    //如果最终不为99,则表示有最优解,否则无解
    if(dp[n][value] != 99)   cout << dp[n][value] << endl;
    else   cout << "0" << endl;
    return 0;
}

程序结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值