1 条题解
-
0
题意简述
给定 个芯片,放置在编号 到 的格点上(每个格点可放任意多个芯片)。允许四种操作(可逆),经过任意次操作后,剩余的最少芯片数称为该放置方案的成本。
求恰好 个芯片、成本恰好为 的放置方案数,对 取模。
数据范围:。核心观察
-
操作可逆性
所有操作在逆操作下封闭,因此我们可以将最终状态反推到初始状态。
最终状态的最简形式是:若干个位于 号点的芯片(因为操作 3、4 可以移动 1、2 号点芯片,且操作 1、2 可以合并芯片到更大位置)。 -
斐波那契关系
$$\text{成本} = \text{最少剩余芯片数} = \min\left\{\sum \text{芯片数} \ \big|\ \sum (\text{芯片数} \times f_{\text{位置}}) = \text{总价值} \right\} $$
通过逆向模拟发现:一个位于位置 的芯片,等价于花费 个位于位置 的芯片,其中 是斐波那契数列()。
原因:操作 1 的逆操作是“将 和 的两个芯片合并成一个位于 的芯片”,这正是斐波那契的递推。
因此:其中“总价值”是初始所有芯片按位置 加权 后得到的 号芯片等价个数。
-
价值上界
,,因此最大总价值 。
且由于 增长很快, 超过 后 ,因此有效位置只到 ,但题目中 ,所以实际只需考虑 。
预处理:最小芯片数对应关系
定义 表示:用若干 ( 可重复)拼出总价值 ,所需的最少芯片数(即最少个数)。
- 初始 ,其余 。
- 完全背包转移:对每个 ,对 从 到 :
注意:这里的 是位置编号, 是价值,一个芯片占据一个位置,所以每加一个 就对应多一个芯片。
因此 的含义是:生成总价值 ,最少需要多少个芯片。
而题目中的“成本”正是这个最少芯片数。
所以:成本 = m 等价于 。
动态规划:统计方案数
我们需要统计有多少种放置方案(即每个位置 上的芯片数),使得:
- 总芯片数 =
- 总价值 满足
设 表示:考虑了前 个位置(滚动数组优化掉第一维),已经放置了 个芯片,总价值为 的方案数。
初始:。
转移:对每个位置 (),枚举这个位置放 个芯片(),则:注意 可到 ,但 上限为 ,直接转移是 不可行。需要优化。
优化转移:采用完全背包式递推。
对每个固定 ,将 看作二维数组( 为芯片数, 为价值)。
转移实际是:对每个 ,增加一个芯片在位置 :并且这个操作可以进行任意次。
这等价于对每个 做二维完全背包:
枚举 从 到 , 从 到 :但注意这样会重复计算顺序?实际上因为我们按位置 顺序处理,且对每个 内部循环 升序、 升序,就能保证每个 的芯片数可以无限取,且芯片总数不断增加。这正是二维完全背包的标准写法。
最终实现:
- 初始化
- 对每个位置 :
- 对 从 到 :
- 对 从 到 :
- 对 从 到 :
- 对 从 到 :
- 最后答案 =
复杂度分析
- 预处理 数组:,可忽略。
- DP:,即 ,在 C++ 中稍大但常数小且取模运算优化后可通过(实际实现加了一些剪枝和循环顺序优化)。
- 空间: 过大,但可以使用滚动数组优化 维度?注意 的转移依赖 本身(完全背包),且 必须用当前 更新后的值,因此不能简单滚动 。但可以注意到 很大而 较小,实际实现中使用了二维数组 ,内存约 ,稍大但仍在可接受范围(代码中使用
long long存储模数,实际可改用int减少一半)。
AC 代码
#include<bits/stdc++.h> using namespace std; const int MOD = 998244353; const int MAXV = 55000; // 最大可能总价值 int f[25]; // 斐波那契数 int c[MAXV + 5]; // c[v] = 拼出价值 v 所需的最小芯片数 int dp[1005][MAXV + 5]; // dp[j][v] = 用 j 个芯片得到总价值 v 的方案数 int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 预处理斐波那契数 f[1] = f[2] = 1; for (int i = 3; i <= 24; i++) { f[i] = f[i-1] + f[i-2]; } // 预处理 c[v]:最少芯片数 memset(c, 0x3f, sizeof(c)); c[0] = 0; for (int i = 1; i <= 10; i++) { // 题目中 x ≤ 10 for (int v = f[i]; v <= MAXV; v++) { if (c[v - f[i]] + 1 < c[v]) { c[v] = c[v - f[i]] + 1; } } } int n, x, m; cin >> n >> x >> m; // DP 计数 dp[0][0] = 1; for (int pos = 1; pos <= x; pos++) { int cost = f[pos]; // 在这个位置放一个芯片的价值 // 完全背包:对每个位置,可以放任意多个芯片 for (int chips = 0; chips < n; chips++) { for (int val = 0; val + cost <= MAXV; val++) { if (dp[chips][val]) { dp[chips + 1][val + cost] = (dp[chips + 1][val + cost] + dp[chips][val]) % MOD; } } } } // 累加答案 long long ans = 0; for (int v = 0; v <= MAXV; v++) { if (c[v] == m) { ans = (ans + dp[n][v]) % MOD; } } cout << ans << "\n"; return 0; } -
- 1
信息
- ID
- 6937
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者