16.1 动态规划引入例子:最少硬币问题

最少硬币问题描述

有n种硬币,面值分别为V1,V2,…,Vn.数量无限。输入非负整数S,请你选用硬币,使其和为S。要求输出最少的硬币组合。

解题思路

先定义一个数组int cnt[M],其中 cnt[i] 是金额 i 对应的最少硬币数量。如果程序能计算出所有的 cnt [i],0< i <M,那么对输入的某个金额 i,只要查 cnt[i] 就得到了答案。

那么我们该如何计算 cnt[i] 呢?cnt[i] 和 cnt[i-1]又是否有关系呢?这里涉及到一个“递推”的思路,从小问题 cnt[i-1] 的解决递推到大问题 cnt[i] 的解决。小问题的解决能推导出大问题的解决。下面我们以5种面值[1,5,10,25,50]的硬币为例,讲解从i-1到 i 的递推过程:

(1) 只使用最小面值的 1 分硬币

在这里插入图片描述

(2)在使用面值1分硬币的基础上,增加使用第二大面值的5分硬币

在这里插入图片描述

(3)继续处理其它面值的硬币

在DP中,把 cnt[i] 这样的记录子问题最优解的数据称为“状态”;从 cnt[i-1] 或 cnt[i-5] 到 cnt[i] 的递推,称为“状态转移”。用前面子问题的结果,推导后续子问题的解,逻辑清晰、计算高效,这就是DP的特点。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int M = 251;                   //定义最大金额
const int N = 5;                     //5种硬币
int type[N] = {1, 5, 10, 25, 50};    //5种面值
int cnt[M];                          //每个金额对应最少的硬币数量
const int INF = 0x1FFFFFFF;

void solve(){
    for(int k = 0; k< M; k++)        //初始值为无穷大
       cnt[k] = INF;
    cnt[0] = 0;
    for(int j = 0; j < N; j++)
        for(int i = type[j]; i < M; i++)
            cnt[i] = min(cnt[i], cnt[i - type[j]] + 1);    //递推式
}
int main(){
    int s;
    solve();               //计算出所有金额对应的最少硬币数量。打表
    while(cin >> s){
        if(cnt[s] == INF) cout <<"No answer" << endl;
        else              cout << cnt[s] << endl;
    }
    return 0;
}

在这里插入图片描述



打印最少硬币的组合

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int M = 251;               //定义最大金额
const int N = 5;                 //5种硬币
int type[N] = {1,5,10,25,50};    //5种面值
int cnt[M];                      //每个金额对应最少的硬币数量
int path[M]={0};                 //记录最小硬币的路径
const int INF = 0x1FFFFFFF;
void solve(){
    for(int k=0; k< M;k++)
        cnt[k] = INF;
    cnt[0]=0;
    for(int j = 0;j < N;j++)
        for(int i = type[j]; i < M; i++)
            if(cnt[i] >  cnt[i - type[j]]+1){
               path[i] = type[j];   //在每个金额上记录路径,即某个硬币的面值
               cnt[i] = cnt[i - type[j]] + 1;  //递推式
            }
}
void print_ans(int *path, int s) {      //打印硬币组合
    while(path[s]!=0 && s>0){
        cout << path[s] << " ";
        s = s - path[s];
    }
}
int main() {
    int s;
    solve();
    while(cin >> s){
        if(cnt[s] == INF)
            cout <<"No answer"<<endl;
        else{
            cout << cnt[s] << endl;     //输出最少硬币个数
            print_ans(path,s);             //打印硬币组合
        }
    }
    return 0;
}
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你说的白是什么白_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值