蓝桥杯 包子凑数(完全背包、裴蜀定理、赛瓦维斯特定理)


一、题目来源

OJ传送门

参考题解

\(2017\)年蓝桥杯软件类省赛\(C++\)语言大学\(A\)组第\(8\)题"包子凑数",一道数论题。

2022年4月青少年蓝桥杯赛第二次省赛初级+中高级组第三题

这也太\(tm\)内卷了,拿这个来考三年级的小孩子!真是太\(BT\)了!还第三题!!!

二、解题思路

  • 裴蜀定理
    简单来说,我们设 \(d = gcd(a,b)\),那么对于方程\(ax+by=d\),一定存在一组整数解。并且对于方程 \(ax + by = z\),如果满足 \(d∣z\),那么方程一定有整数解,否则无整数解。

根据裴蜀定理,当所有种类的蒸笼包子数的最大公约数不为\(1\)时,凑不出的包子数为无限多,因为无论蒸笼怎么组合都必须得是\(gcd\)得倍数,\(gcd\)不为\(1\)一定会有凑不出的。

三、实现代码

#include 
using namespace std;
/**
 测试用例
 2
 3 5

 答案
 4
 */
typedef long long LL;
const int N = 1e6 + 5;

LL f[N];  //表示凑出j个包子的方法总数
int a[N]; //每种规格中包子的数量
int ans;  //组装不出来的包子个数

//最大公约数,辗转相除法
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    //赛瓦维斯特定理 :  两个互质数a,b表示不出来的最大数字是 a?b?a?b
    //下面排序后找出极小值与次小值,它们的乘积再减去极小和次小,就是极限值
    sort(a + 1, a + 1 + n);
    int m1 = a[1], m2 = a[2]; //最小和次小
    int M = m1 * m2 - m1 - m2;

    //求出所有数字的最大公约数
    int g = a[1];
    for (int i = 2; i <= n; i++) g = gcd(g, a[i]);

    /*裴蜀定理:如果所有数字的最大公约数大于1,那么必然存在无法表示出的数字。
     不互质栗子:
     比如3 和 6      两种规格,能组装的就是 3,6,9,12,15,这个变化就是它们的最大公约数3。
     比如3 和 6 和 9 三种规格, 能组装的也是 3,6,9,12,15,这个变化就是它们的最大公约数3。
     此时 ,类似于 1,2,4,5,7,...之类的数字均是无法凑出的,有无穷多个。

     互质栗子:
     比如 3 和  4   两种规格, 能组装的就是 3,4,6,7,8,9,10,11,12...., 根据赛瓦维斯特定理,我们知道,最大的不能组装的数字是3*4-3-4=5
     比如 3 和  4 和 6 三种规格,因为3,4就能组装出5以上的所有数字,所以我们只需要从最小a[1]到 M 中查找即可。
    */
    if (g > 1) {
        cout << "INF" << endl; // cout << -1 << endl; //青少组要求输出-1
        return 0;
    }

    /**
      完全背包
      f[j]表示凑出j个包子的方法总数,简单说就是如果f[j]>0就凑得出,f[j]=0就凑不出。
      递推式 f[j]=f[j?第i种蒸笼包子数]+1 (f[j?第i种蒸笼包子数]>=1)
      当j?第i种蒸笼包子数是可以凑成的,那么只需对其+第i种蒸笼包子数就可以凑成j个包子数。
     */
    f[0] = 1; //  0个包子这个状态是合法的,是可以到达的状态。而且只能是一种方案,就是不管是哪种规格,一概不要
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= M; j++)
            if (j >= a[i] && f[j - a[i]]) f[j]++;

    //枚举每个可能的数字,如果这个数字存在1种以上的方案,就是可以凑出,否则就是无法凑出
    for (int i = 1; i <= M; i++)
        if (!f[i]) ans++;

    //输出答案
    cout << ans << endl;
    return 0;
}
  • 赛瓦维斯特定理
    如果能够凑出的数有限,那么到达某个界限后都是凑得出的,这个界限为\(a*b-a-b\)。这里由于有多个数字,我们可以采用最大做为\(a\),次大做为\(b\)来求解。

  • 递推关系
    \(dp[i]\)表示凑出\(i\)个包子的方法总数,简单说就是如果其\(>=1\)就凑得出,\(==0\)就凑不出。

状态转移方程: \(dp[i]=dp[i-\)\(j\)种蒸笼包子数\(]+1\) (\(dp[i-\)\(j\)种蒸笼包子数\(]>=1\))
\(i-\)\(j\)种蒸笼包子数是可以凑成的,那么只需对其+第\(j\)种蒸笼包子数就可以凑成\(i\)个包子数。