1 条题解
-
0
「PA 2020」Cukierki 题解
算法思路
本题使用动态规划和背包问题的变种来解决子集选择问题。核心思想是判断哪些子集能够表示从 到子集和的所有数值。
关键观察
1. 问题转化
一个子集 满足条件当且仅当:对于任意 ,都能用 的某个子集表示出 。
2. 充要条件
将糖果按重量排序后,子集 (已排序)满足条件当且仅当:
$$a_{i+1} \leq 1 + \sum_{j=1}^i a_j \quad \text{对于所有 } i $$代码解析
排序预处理
sort(a + 1, a + n + 1);将糖果按数量从小到大排序,这是满足条件的关键。
动态规划状态
ll f[maxn * maxn]; // f[j] 表示当前能表示[1,j]的所有子集方案数 f[0] = 1; // 空集的方案数为1动态规划转移
for (int i = 0; i < n; i++) { sum += a[i]; for (int j = 5000; j >= a[i + 1] - 1; j--) { f[min(j + a[i + 1], 5000)] = (f[min(j + a[i + 1], 5000)] + f[j]) % mod; } }转移解释:
- 当前考虑第 袋糖果
- 如果当前能表示区间 ,且
- 那么加入 后能表示区间
- 限制在 以内因为
答案统计
for (int i = 1; i <= 5000; i++) ans += f[i], ans %= mod;统计所有非空子集的方案数。
算法正确性
条件验证
对于已排序的子集 :
- 初始能表示
- 加入 后能表示 ,需要 才能表示
- 加入 时,如果 ,则能表示
- 依此类推,保证连续性
动态规划正确性
- 记录当前能表示到 的方案数
- 转移时确保新加入的糖果不会破坏连续性条件
复杂度分析
- 排序:
- 动态规划:
- 总复杂度:
关键技巧
- 排序预处理:确保糖果按升序处理
- 连续性检查:通过 保证能表示所有数值
- 滚动数组:使用一维数组优化空间
- 模运算处理:正确处理大数取模
- 1
信息
- ID
- 3639
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者