完全背包可达性筛求最大凑不出的块数:gcd 不为 1 时直接输出 0,否则在 65536 内筛出所有可表示数。
OJ: roj
题目 ID: 2061
难度:普及
标签:完全背包数论打表
创建: 2026-10-01 05:43
更新: 2026-10-06 10:37
题目描述
给定
思路
先求所有盒容量的最大公约数
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 05:50
# update_at: 2026-10-01 05:50
import sys
from functools import reduce
from math import gcd
# 不可表示数的上界:互质的 a,b 最大凑不出的数是 ab-1,盒容量 ≤ 256,故 256*256 内必出答案
LIMIT = 256 * 256
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
boxes = [next(data) for _ in range(n)]
# 所有盒容量的公约数 g>1 时,买到的块数永远是 g 的倍数,其余需求无限多且都没有最大者
if reduce(gcd, boxes) != 1:
print(0)
return
# 可达性筛:s 块能凑出 ⇔ 存在盒子 b,使 s-b 块能凑出(0 块是唯一初始事实)
reachable = bytearray(LIMIT + 1)
reachable[0] = 1
for s in range(1, LIMIT + 1):
reachable[s] = any(b <= s and reachable[s - b] for b in boxes)
# 找不到不可表示的数说明范围内全能凑出,输出 0
print(max((s for s in range(1, LIMIT + 1) if not reachable[s]), default=0))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 10:36
* update_at: 2026-10-06 10:36
*/
#include <iostream>
using namespace std;
typedef long long ll;
const int LIMIT = 256 * 256; // 盒容量 ≤256,互质时最大凑不出的数 < 256*256
int n;
int b[15]; // 盒容量
bool reach[LIMIT + 1]; // reach[s] 表示 s 块能否凑出
ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> b[i];
// 所有盒容量的最大公约数 g > 1 时,非 g 倍数的块数永远买不到且无最大值
ll g = b[1];
for (int i = 2; i <= n; ++i) g = gcd(g, b[i]);
if (g != 1) {
cout << 0 << "\n";
return 0;
}
// 完全背包可达性筛:s 能凑出 ⇔ 存在盒子 b,使 s-b 能凑出
reach[0] = true;
for (int s = 1; s <= LIMIT; ++s) {
bool ok = false;
for (int i = 1; i <= n; ++i) {
if (b[i] <= s && reach[s - b[i]]) {
ok = true;
break;
}
}
reach[s] = ok;
}
int ans = 0;
for (int s = 1; s <= LIMIT; ++s)
if (!reach[s]) ans = s; // 取最大不可达数
cout << ans << "\n";
return 0;
}