入门经典--硬币问题dp递归加递推

本文介绍了一种使用动态规划解决背包问题的方法,通过递归和迭代两种方式实现了最小数量和最大数量物品选择的算法。该文重点讲解了如何利用记忆化搜索优化计算过程,并对比了递归与迭代的不同实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
int v[101],s,n,mini[10001],maxn[10001],visi[10001];

int MAX(int a,int b)
{
    return a>b?a:b;
}
int MINI(int a,int b)
{
    return a<b?a:b;
}

int dp_mini(int ss)
{
    if(visi[ss]) return mini[ss];
    visi[ss] = 1;
    mini[ss] = (1<<30);
    for(int i=1;i<=n;++i)
        if(ss>=v[i]) mini[ss] = MINI(mini[ss],dp_mini(ss-v[i])+1);
    return mini[ss];
}

int dp_max(int ss)
{
    if(visi[ss]) return maxn[ss];
    visi[ss] = 1;
    maxn[ss] = -(1<<30);
    for(int i=1;i<=n;++i)
        if(ss >= v[i]) maxn[ss] = MAX(maxn[ss],dp_max(ss-v[i])+1);
    return maxn[ss];
}

int solve_mini()
{
    for(int i=1;i<=s;++i)
        mini[i] = (1<<30);
    mini[0] = 0;
    for(int i=1;i<=s;++i)
        for(int j=1;j<=n;++j)
            if(i >= v[j]) mini[i] = MINI(mini[i],mini[i-v[j]]+1);
    return mini[s];
}

int solve_max()
{
    for(int i=1;i<=s;++i)
        maxn[i] = -(1<<30);
    maxn[0] = 0;
    for(int i=1;i<=s;++i)
        for(int j=1;j<=n;++j)
            if(i>=v[j]) maxn[i] = MAX(maxn[i],maxn[i-v[j]]+1);
    return maxn[s];
}

int main()
{
    scanf("%d %d",&n,&s);
    for(int i=1;i<=n;++i)
        scanf("%d",&v[i]);
    memset(visi,0,sizeof(visi));
    visi[0]=1;//这一步初始化很关键,原书并未强调
    int mini1 = dp_mini(s);
    memset(visi,0,sizeof(visi));
    visi[0]=1;
    int max1 = dp_max(s);
    int mini2 = solve_mini();
    int max2 = solve_max();
    printf("%d %d\n%d %d\n",mini1,max1,mini2,max2);

    return 0;
}

零钱兑换问题是一个经典的动态规划问题,通常用于练习递归算法和优化策略。目标是使用给定的硬币面额(例如1元、2元和5元)来凑出指定的总金额(如11元),同时找到所需的最小硬币数量。这个问题可以使用回溯(也称递归)的方法来解决。 **解题思路:递归法** 1. 定义状态:设 `dp[i]` 表示使用给定面额的硬币能够凑出 `i` 元所需的最小硬币数量。初始状态 `dp = 0`,因为不需要任何硬币来凑0元。 2. 递归规则:对于每个 `i`(从1到总金额),尝试用三种面额中最小的一种来替换 `i` 元,然后更新 `dp[i]` 的值。具体步骤如下: a. 如果 `i` 是某个面额的倍数,直接用该面额的硬币,即 `dp[i] = dp[i - coin_value] + 1`,这里 `coin_value` 是1, 2或5。 b. 否则,寻找小于 `i` 的最大面额 `j`(例如5),尝试将 `j` 添到已有的解决方案中,即 `dp[i] = min(dp[i], dp[i - j] + 1)`。 3. 递归结束条件:当 `i` 大于总金额时,返回 `dp[i]`,否则继续递归直到 `i` 为0。 **重复计算次数和递归图解过程:** 递归过程中存在大量的重复计算,比如多次尝试用1元硬币凑11元的情况。可以利用记忆化搜索(动态规划)来避免重复计算。在每个状态 `dp[i]` 记录之前的结果,只有在首次访问时才会计算。 在递归图解过程中,通常会画出一个表格(类似数组),其中行代表金额,列代表可用的面额,然后填充递推过程中的 dp 值。从左上角开始,通过横向和纵向的递推,逐步填充整个表格。每个格子的填充都是基于左上角到当前位置的最小子集解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值