【洛谷】P8646 [蓝桥杯 2017 省 AB] 包子凑数 的题解

【洛谷】P8646 [蓝桥杯 2017 省 AB] 包子凑数 的题解

题目传送门

思路

题上要问的是凑不出的数目,如果直接推断哪个数不可能凑出是非常困难的。

所以我们考虑反向解决这个问题:

在合适的范围内将所有可以凑出的数枚举出来,然后用总数减去凑出的数的数目就是答案了。

至于合适的范围这件事情,可以想的:

两个数 n n n m m m 最大的不可能凑出的数是 n × m − n − m n \times m-n-m n×mnm,题上给出的 a i a_i ai 最大是 100 100 100,抛去 n n n m m m 不可能同时为 100 100 100 这件事情,最大的不可能凑出的数也不会超过 100 × 100 − 200 100 \times 100-200 100×100200

在枚举之前还有一个问题,INF

什么情况下会有无数个数无法凑出呢?

样例二的输入其实已经给出了答案,如果所有的 a i a_i ai 不互质,也就是说它们都是某一个大于 1 1 1 的数的倍数,就会有无数个的数无法凑出,毕竟这样的 a i a_i ai 所能构造出的数都是它们 gcd ⁡ \gcd gcd 的倍数。

我们用一个 v i s vis vis 数组来标记每一个数能否被凑出,首先 0 0 0 肯定是可以的,也就是不买。

接下来我们用一个类似动规的思想去思考枚举问题:

如果当前的某个数 j j j 可以被凑出,那么由这个数与每个 a i a_i ai 相加的结果 j + a i j + a_i j+ai 也可以被凑出,所以就有了公式:

if(vis[j]) {
	vis[j + a[i]] = true;
}

有了这个公式以后,思路也就明朗了,对 j j j 0 0 0 m a x n maxn maxn 的循环, 对 i i i 1 1 1 n n n 的循环就好了。

代码

#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
int n, a[105];
bool vis[10005];
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
int main() {
	//freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
	cin >> n >> a[1];
	int gcdn = a[1];
	for(int i = 2; i <= n; i ++) {
		cin >> a[i];
		gcdn = gcd(gcdn, a[i]);
	}
	if(gcdn != 1) {
		cout << "INF"; 
		return 0;
	}
	vis[0] = true;
	for(int i = 1; i <= n; i ++) {
		for(int j = 0; j + a[i] <= 10000; j ++) {
			if(vis[j]) vis[j + a[i]] = true;
		}
	}
	int res = 0;
	for(int i = 1; i <= 10000; i ++) {
		if(!vis[i]) res ++;
	}
	cout << res;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值