小明开了一家糖果店。
他别出心裁:把水果糖包成 4 颗一包和 7 颗一包的两种。
糖果不能拆包卖。
小朋友来买糖的时候,他就用这两种包装来组合。
当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。
你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是 17。
大于 17 的任何数字都可以用 4 和 7 组合出来。
本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。
输入格式
两个正整数 n,m,表示每种包装中糖的颗数。
输出格式
一个正整数,表示最大不能买到的糖数。
数据范围
2≤n,m≤1000,
保证数据一定有解。
输入样例:
4 7
输出样例:
17
超时
思路:对于数据 4 7,发现 18 19 20 21,然后 22(18 + 4)…… 四个一循环。所以当相邻四个数可以连续用 n 和 m 表示时,最后一个数减去间隔(n 和 m 较小值)即可得到最大不能组合出的数字。
18 19 20 21 | 22 23 24 25
4 1 3 5 0 | 2 4 6 1
7 2 1 0 3 | 2 1 0 3
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int gap = n < m ? n : m;
int cnt = 0;
for (int num = n + m;; ++num) { //2 <= n, m,所以从 n + m 开始
bool exist = false;
for (int i = 0; i <= num / n; ++i) { //倍数 i 最大为 num / n
for (int j = 0; j <= num / m; ++j) {
if (i * n + j * m == num) {
cnt++;
exist = true;
break;
}
}
if (exist) break;
}
if (!exist) cnt = 0; //不连续,置零
if (cnt == gap) {
cout << num - gap;
break;
}
}
return 0;
}
AC:dp + vector
思路:对双重循环进行了优化,从 n 和 m 的较小数开始遍历后面的所有数 i,如果 i - n 或 i - m 是组合数,则该数也是组合数。
因为不确定多久才会找到,所以使用 vector 的 resize 函数创建很多元素(会自动初始化为 0),如果遍历的时候超过了 vector 的大小,则进行重新 resize。尽管当 vector 的大小大于容量时会自动进行重新分配,但这样就无法通过 [] 进行访问后面的元素,而必须使用 push_back()。
这里使用了 100000,如果数字太大内存会超限。另外取 100 会超时,因为每次都会进行重新分配空间,是很耗时的,而 1000 则没有问题。
另外对于输入数据 107 983,如果 vector 最初大小取 500,则会输出 104198,而答案是 104091,不知道为什么。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int gap = n < m ? n : m;
int cnt = 0;
vector<int> dp(100000); //相当于 vector<int> dp; dp.resize(100000); 此时大小和容量均为 100000
dp[n] = dp[m] = 1;
int i;
int min = gap;
for (i = min;; ++i) { //对于 4 99,100 也可以组合,所以从较小值开始,这样 4 的倍数包括 96 也会组合,100 也会组合
if (i >= dp.size()) dp.resize(dp.size() * 2); //i 为 dp 下标,最大下标为 dp.size() - 1
if ((i >= n && dp[i - n]) || (i >= m && dp[i - m])) { //必须保证防止越界
dp[i] = 1;
cnt++;
if (cnt == gap) break;
} else
cnt = 0;
}
cout << i - gap;
return 0;
}
输出测试
for (int j = 1; j <= i; ++j) {
if (j <= 9) cout << '0';
cout << j << ' ';
}
cout << '\n';
for (int j = 1; j <= i; ++j) cout << ' ' << dp[j] << ' ';
cout << '\n';
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21
0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 1 1 1 1
AC:dp + bitset
缺点:bitset 不能改变大小,必须且只能在初始化的时候指定。所以不知道该取多少大小。
疑惑:vector 65ms,而 bitset 147ms
#include <bitset>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int gap = n < m ? n : m;
int cnt = 0;
bitset<1000000> dp;
dp[n] = dp[m] = 1;
int i;
int min = gap;
for (i = min;; ++i) { //对于 4 99,100 也可以组合,所以从较小值开始,这样 4 的倍数包括 96 也会组合,100 也会组合
if ((i >= n && dp[i - n]) || (i >= m && dp[i - m])) { //必须保证防止越界
dp[i] = 1;
cnt++;
if (cnt == gap) break;
} else
cnt = 0;
}
cout << i - gap;
return 0;
}