PTA -- L3-001凑零钱 C++

文章介绍了如何使用动态规划解决01背包问题的扩展题,通过数组排序和后向前推的方式找到表示给定金额的最小硬币组合。
摘要由CSDN通过智能技术生成

题目如上,该题目是01背包的一个扩展题,题解如下:

1、用一个数组a[ ]存储所有硬币的面值

2、对a数组进行排序(因为排序不会影响01背包的结果,而且题目要求输出最小序列,排序后方便后面做题)

3、用一个数组f[N][N]表示动态规划

状态表示:f[i][j]
    集合:表示只从第i个硬币及它之后的硬币中选,总钱数恰好为j的方案数
    属性:count(数量)
状态计算:
    不选第i个硬币:f[i+1][j]
    选第i个硬币:f[i+1][j-a[i]](前提:j>=a[i])
    总结: f[i][j]=f[i+1][j]+f[i+1][j-a[i]]

这里为什么要从后往前推呢?因为我们要输出最小序列,从小到大排序后做动态规划,我们从前往后枚举时,如果当前数可选(假设当前枚举到了第i个数,即j>=a[i]且f[i+1][j-a[i]]不为0,如果f[i+1][j-a[i]]为0,则表示从第i+1个硬币到不了第i个硬币),则必选(输出a[i]),否则必不选

下面的f[1][m]表示如果所有的硬币可以表示为恰好m元钱时,则进入循环,否则输出No Solution

int i=1,j=m;
if(f[1][m]){
        while(i<=n){
            //如果j>=a[i]且可以由后一个状态转移过来,则必选这个数
            if(j>=a[i]&&f[i+1][j-a[i]]){
                cout<<a[i];
                j-=a[i];
                if(j!=0)cout<<" ";
            }
            i++;
        }
        cout<<endl;
}else cout<<"No Solution"<<endl;

f数组用之前要初始化,由于i是倒序枚举的,所以在状态表示中我们要用到i+1,所以对于所有的i从1~n+1,f[i][0](从第i个及i之后的硬币中选,总数刚好为0元,就只有一种情况,就是一个硬币都不选)所以都要初始化为1

#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010,M=110;
int a[N];
int f[N][M];
int n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+n+1);//由于求解要输出最小序列,在一开始就排序会好处理一点
    for(int i=1;i<=n+1;i++)f[i][0]=1;
    for(int i=n;i>=1;i--){
        for(int j=m;j>=0;j--){//由于f现在是二维的,j从m或从0开始枚举都可以
            f[i][j]=f[i+1][j];
            if(j>=a[i])f[i][j]+=f[i+1][j-a[i]];
        }
    }
    int i=1,j=m;
    if(f[1][m]){
        while(i<=n){
            //如果j>=a[i]且可以由后一个状态转移过来,则必选这个数(因为是从小到大枚举)
            if(j>=a[i]&&f[i+1][j-a[i]]){
                cout<<a[i];
                j-=a[i];
                if(j!=0)cout<<" ";
            }
            i++;
        }
        cout<<endl;
    }else cout<<"No Solution"<<endl;
    return 0;
}

这样题目就完成啦!如果大家有更好的想法的话,欢迎大家的指点!!!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值