蓝桥杯第四届真题 - 买不到的数目(dp)

小明开了一家糖果店。

他别出心裁:把水果糖包成 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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值