[蓝桥杯2017初赛]包子凑数

题目描述

小明几乎每天早晨都会在一家包子铺吃早餐。这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子
每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会选出若干笼包子来,使得这若干笼中恰好一共有X个包子。
比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。
比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。

翻译成人话

给出N个正整数,每个整数有无限多个。任取一些整数相加,得到大于等于1的数。问有多少个数得不到。如果得不到的有无限多个,输出“INF”;否则输出得不到的数量。
例如给出4、5,它们组合不能得到的数有6个:1, 2, 3, 6, 7, 11。其他的数都能通过组合得到。

题解

首先,判断是不是INF

先考虑只有两个正整数的时候,答案假如是INF,也即方程 ax + by = c 在c的所有取值下都没有整数解
  如果你不知道这个方程什么时候会有整数解,请看这里:设a ,b是整数且gcd (a , b) = d,如果d不能整除c,那么方程ax + by = c没有整数解,反之能整除的话,方程存在无穷多个整数解。
  如果d=1,那ax+by就可以得到所有整数。
  那么,题目应该只有两种状态,也就是0和INF才对,为什么还有统计个数呢,别急,ax+by确实可以获得所有整数,但是此时想想x,y可以是负整数,而在本题目中,包子显然不能是负数。
  假如c比较小,那么方程可能没有正整数解,但是当c很大时,肯定有x、y的非负整数解。这个可以证明:
  gcd(a,b)=1时,ax+by=c的通解是:
x=x0+bn
y=y0−an
再加上ax0+by0=c可以得到
by=c−ax0−abn
  取x的一个正整数值,此时如果c够大,那么by就是正的,y自然也是正的

统计无法组合得到的个数

按照前面的推论,c足够大时不需要考虑,那只需考虑c在某一范围之内无法组合的数字。
用dp[i]=1表示第i个整数被计算出来了,最后统计没有被算过的dp[i]数量。

    for(int i = 1; i <= n; i++) {  //dp[i]:第i个整数是否被计算出来
        dp[a[i]]= 1;
        for(int j = 0; j + a[i] < 10000; j++)
            if(dp[j]) {
                dp[j + a[i]] = 1;
            }
    }

最后统计

    for(int i = 1; i < 10000; i++)
        if(dp[i] == 0) ans++;

完整代码

//newoj User: ln2037
#include<bits/stdc++.h>
using namespace std;
const int maxn = 13000;
int a[maxn];
int dp[maxn]={0};

int main() {
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    int g = a[1];
    for(int i = 2; i <= n; i++)  //计算所有数的gcd
        g = __gcd(g, a[i]);
    if(g != 1) {
        cout << "INF";
        return 0;
    }
    for(int i = 1; i <= n; i++) {  //dp[i]:第i个整数是否被计算出来
        dp[a[i]]= 1;
        for(int j = 0; j + a[i] < 10000; j++)
            if(dp[j]) {
                dp[j + a[i]] = 1;
            }
    }
    int ans = 0;
    for(int i = 1; i < 10000; i++)
        if(dp[i] == 0) ans++;
    cout << ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值