蓝桥98包子凑数

题目

包子凑数https://www.lanqiao.cn/problems/98/learning/?page=1&first_category_id=1&tag_category_id=59
题目描述
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 N 种蒸笼,其中第 𝑖 种蒸笼恰好能放 𝐴𝑖个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买 𝑋 个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有 𝑋 个包子。比如一共有 3 种蒸笼,分别能放 3、4 和 5 个包子。当顾客想买 11 个包子时,大叔就会选 2 笼 3 个的再加 1 笼 5 个的(也可能选出 1 笼 3 个的再加 2 笼 4 个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有 3 种蒸笼,分别能放 4、5 和 6 个包子。而顾客想买 7 个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入描述
第一行包含一个整数 N (1≤𝑁≤100)。
以下 N 行每行包含一个整数 𝐴𝑖(1≤𝐴𝑖≤100)。
输出描述
一个整数代表答案。如果凑不出的数目有无限多个,输出 INF。
输入输出样例
示例 1
输入
2
4
5
输出
6
样例说明
凑不出的数目包括:1, 2, 3, 6, 7, 11。
示例 2
输入
2
4
6
输出
INF
样例说明
所有奇数都凑不出来,所以有无限多个
运行限制
最大运行时间:1s
最大运行内存: 256M

我的想法

我看了

lanqiao2094248803

题解:

解题思路

这个是从一个简单dp问题演化而来的: 手里分别有无数张面额为x,y,z元的纸币,请问凑出i元的方法有多少种:

动态规划表达:dp[i],dp[i]表示凑出i元有多少种可能-----------------------------------------------------------------------------------------实现: dp[i] = dp[i]+dp[i-x]+dp[i-y]+dp[i-z]; ::意为, 当前如果要凑i元钱,凑出之前必然是 i-x,i-y,i-z元中的其中一个,如果i-x,i-y,i-z中的任意一个能凑出来,dp[i]就可以凑出来

学会了这道题的解法:

包子凑数这道题关心的是哪些金额无法凑出。所以不用完全计算凑出的方法有多少种。

一个更直接的方法是使用一个布尔数组canReach[i]来表示金额i是否可以凑出

C++中布尔类型可以隐式转换为整数(true为1,false为0),所以我可以使用一个整数数组dp,其中dp[i]表示金额i是否可以凑出(如果dp[i]为正数,则表示可以;如果为0,则表示不可以)

其中,maxAmount的值为10000,因为题目限制最多100种蒸笼,每个蒸笼最多100个包子。

而无法凑出的数超过了一半的maxAmount,我就认为它们是“无限多”的。

初始化canReach数组,其中canReach[0] = true,其余元素为false

假设capacities是一个包含不同蒸笼(或容器)容量的数组,例如[1, 2, 5]

一旦为当前包子数i找到了一个可行的组合(即canReach[i] = true),就通过break语句跳出内层循环,因为没有必要再检查其他容量了。这个优化减少了不必要的计算。

举例解释动态规划过程

假设capacities = [1, 2, 5]且maxAmount = 10。

初始状态:
canReach = [true, false, false, false, false, false, false, false, false, false, false]
处理包子数1:
检查1,发现canReach[1-1] = true,因此canReach[1] = true。
更新后:[true, true, false, false, false, false, false, false, false, false, false]
处理包子数2:
检查1,canReach[2-1] = true,但已处理过。
检查2,canReach[2-2] = true,因此canReach[2] = true。
更新后:[true, true, true, false, false, false, false, false, false, false, false]
继续处理直到maxAmount:
对于每个包子数,重复上述过程,直到maxAmount。
最终结果:
canReach数组将展示哪些包子数可以被凑出。

代码

#include <iostream>  
#include <vector>  
#include <algorithm> // 用于std::max  
  
using namespace std;  
  
int main() {  
    int N;  
    cin >> N; // 蒸笼种类数  
    vector<int> capacities(N);  
    for (int i = 0; i < N; ++i) {  
        cin >> capacities[i]; // 读取每种蒸笼的容量  
    }  
  
    // 假设最大金额不超过10000(这个值可以根据实际情况调整)  
    int maxAmount = 10000;  
    vector<bool> canReach(maxAmount + 1, false);  
    canReach[0] = true; // 0元总是可以凑出的  
  
    // 动态规划过程  
    for (int i = 1; i <= maxAmount; ++i) {  
        for (int cap : capacities) {  
            if (i >= cap && canReach[i - cap]) {  
                canReach[i] = true;  
                break; // 一旦发现一种方式可以凑出i,就无需再检查其他蒸笼  
            }  
        }  
    }  
  
    // 计算无法凑出的金额数量  
    int count = 0;  
    for (int i = 1; i <= maxAmount; ++i) {  
        if (!canReach[i]) {  
            ++count;  
        }  
    }  
  
    // 如果存在一种容量是其他所有容量的公因数(除了1),则可能无法凑出的数是无限的  
    // 但为了简化问题,我们只计算在给定的maxAmount内无法凑出的数的数量  
    // 如果需要判断“无限多”,则需要更复杂的分析(通常涉及数学上的证明)  
  
    if (count >= maxAmount / 2) { // 一个粗略的估计,用于判断是否“无限多”  
        cout << "INF" << endl;  
    } else {  
        cout << count << endl;  
    }  
  
    return 0;  
}

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值