2017年第八届蓝桥杯c/c++B组 包子凑数 DP + 数学方法

题目提交点

点我进入官网提交该题

题目

在这里插入图片描述

思路

首先我们考虑,什么情况下要输出INF。
我们由裴蜀定理可以知道: a ∗ x + b ∗ y = d a*x + b * y = d ax+by=d, 那么我们可以扩展到n个数之间: x 1 ∗ a 1 + x 2 ∗ a 2 + x 3 ∗ a 3 … … + x n ∗ a n = d x_1* a_1 +x_2* a_2 + x_3* a_3 ……+x_n* a_n = d x1a1+x2a2+x3a3+xnan=d,d为他们的最大公约数,那么我们就可以知道:能凑成数只有d的倍数,当 d = 1 d = 1 d=1的时候能凑成的数是有限个(因为我们的 x i x_i xi得是正数),当 d ≠ 1 d \neq1 d=1的时候,他只能凑出d的倍数,不能凑出来的数是无穷多个,此时输出INF。
然后我们考虑有限多个,因为包子是无限多个,所以我们就是完全背包的题目。那我们最多凑不出多少个包子呢?
做过 [蓝桥杯][2013年第四届真题]买不到的数目的朋友应该知道,求两个不互质的数凑不出来的数的数量格式应该是 ( p − 1 ) ∗ ( q − 1 ) − 1 (p -1) * (q - 1) - 1 (p1)(q1)1
当有n个数互质的时候凑不出来的数只会越来越来少,所以他最大不会超过 ( 100 − 1 ) ∗ ( 100 − 1 ) − 1 (100 -1) * (100 - 1) - 1 (1001)(1001)1,索性我们直接设置为10000。

DP分析:
在这里插入图片描述
有三重循环,会超时,所以我们得要将我们的背包优化一下:
f [ i , j ] = f [ i − 1 , j ] ∣ f [ i − 1 , j − a ] ∣ f [ i − 1 , j − 2 ∗ a ] ∣ … … ∣ f [ i − 1 , j − k ∗ a ] f[i, j] = f[i-1, j ] | f[i - 1, j - a] | f[i - 1, j - 2*a]| ……|f[i - 1, j - k*a] f[i,j]=f[i1,j]f[i1,ja]f[i1,j2a]f[i1,jka]
f [ i , j − a ] = f [ i − 1 , j − a ] ∣ f [ i − 1 , j − 2 ∗ a ] ∣ … … ∣ f [ i − 1 , j − k ∗ a ] f[i, j - a] = f[i - 1, j - a] | f[i - 1, j - 2*a]| ……|f[i - 1, j - k*a] f[i,ja]=f[i1,ja]f[i1,j2a]f[i1,jka]
所以可以得出: f [ i , j ] = f [ i − 1 , j ] ∣ f [ i , j − a ] f[i, j] = f[i-1, j] | f[i, j-a] f[i,j]=f[i1,j]f[i,ja]

初始化: f [ 0 ] [ 0 ] = 1 f[0][0] = 1 f[0][0]=1

AC代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

const int N = 10000 + 10, M = 100 + 10;
int a[M];
bool f[N];

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false); // 取消cin与stdin 的同步
    cout.tie(0), cin.tie(0);

    int n, d=0;
    cin >> n;
    _rep(i, 1, n) {
        cin >> a[i];
        d = gcd(d, a[i]);
    }

    if (d != 1) cout << "INF" << ENDL;
    else {
        f[0] = true;
        _rep(i, 1, n) _rep(j, a[i], N - 1) f[j] |= f[j - a[i]];

        int ans = 0;
        _rep(i, 1, N - 1) if (!f[i]) ans++;
        cout << ans << ENDL;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值