PAT L3-001 凑零钱(01背包(布尔背包)+记录路径)

127 篇文章 0 订阅
48 篇文章 0 订阅

L3-001. 凑零钱

时间限制:200 ms
内存限制:65536 kB
代码长度限制:8000 B
判题程序:Standard
作者:陈越

韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有104枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。

输入格式:

输入第一行给出两个正整数:N(<=104)是硬币的总个数,M(<=102)是韩梅梅要付的款额。第二行给出N枚硬币的正整数面值。数字间以空格分隔。

输出格式:

在一行中输出硬币的面值 V1 <= V2 <= … <= Vk,满足条件 V1 + V2 + … + Vk = M。数字间以1个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出“No Solution”。

注:

我们说序列{A[1], A[2], …}比{B[1], B[2], …}“小”,是指存在 k >= 1 使得 A[i]=B[i] 对所有 i < k 成立,并且 A[k] < B[k]。

输入样例1:

8 9
5 9 8 7 2 3 4 1

输出样例1:

1 3 5

输入样例2:

4 8
7 2 4 3

输出样例2:

No Solution

问题分析

学长和窝说这是01背包的变种布尔背包…….
题意:给n个硬币,问能否组成价值m。
那该怎么设计状态呢?
我感觉就是01背包的思想。可以这样,第i个硬币能否组成m,如果第i个硬币可以组成m,那么状态dp[j-coin[i]肯定就为true,否则为false,即表示当前硬币不可达m。如果选第i个,那么问题就变成i-1个硬币组成v-coin[i]的问题,不选就是i-1个硬币组成v的问题。边界状态肯定是硬币价值为0啦,0对于m一定是可达的,所以要初始dp[0] = true;所以状态转移方程为
dp[i][j] = dp[i-1][j] | dp[i-1][j-coin[i]]; 或者简化为一维,
dp[j] = dp[j] | dp[j-coin[i]]; (0 <= i <= n-1, 0 <= j <= m)

剩下的就是路径记录问题了,只要记住当前状态是由哪个状态转移过来的即可,输出coin[i]
ok,接下来是AC code(^_^)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const  int N = 1e4+3,M = 400;
bool path[N][M],dp[N];
int coin[N];
bool cmp(int a,int b){
    return a>b;
}

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i = 0; i < n; ++i){
        cin>>coin[i];
    }
    dp[0] = true; //m+0一定还可以组成m,即0这个数可达(边界状态)
    sort(coin,coin+n,cmp); //这里我们从大到小排序,方便输出,到后面如果有更小的方案会覆盖
    for(int i = 0; i < n; ++i){
        for(int j = m; j >= coin[i]; j--){
//            dp[j] = dp[j]|dp[j-coin[i]];
            if(dp[j-coin[i]]){ // 如果当前选择当前硬币可组成m
                dp[j] = true;
                path[i][j] = true; //记录路径
            }
        }
    }
    if(dp[m]==false) cout<<"No Solution"<<endl;
    else{
        int i = n-1, j = m, flag = 0;
        while (i>=0&&j>=0){
            if(path[i][j]){ //当前状态可选
                if(flag==0) { printf("%d",coin[i]); flag = 1;}
                else
                    printf(" %d",coin[i]);
                j -= coin[i]; //要清楚当前状态是怎么转移过来的
            }
            i--;
        }
        puts("");
    }
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值