1 条题解
-
0
题目分析
有一个长度为 的尺子,刻度编号为 。初始时每个刻度上有一个橡皮,系统平衡(力矩和为0)。现在要拿走 个橡皮,要求拿走后的系统仍然保持平衡。
我们需要计算满足条件的方案数,结果对 取模。
问题转化
设拿走的 个橡皮的位置为 (每个 ),则平衡条件为:
通过坐标平移 ,问题转化为:
- 每个
解题思路
使用动态规划求解:
设
f[i][j]表示用 个在 范围内的整数凑出和为 的方案数。递推关系基于整数划分的经典方法,并加入范围限制。
关键公式
递推公式:
f[i][j] = f[i-j][j] + f[i-j][j-1] - f[i-(n+1)*2][j-1]其中:
f[i-j][j]:所有数都 ≥ 1 的情况f[i-j][j-1]:包含0的情况- 减去
f[i-(n+1)*2][j-1]:排除超出范围的情况
完整代码
#include <cstdio> #include <iostream> #include <cstring> using namespace std; const int MAXN = 1e4 + 10; int main() { int T; cin >> T; while (T--) { int n, k, PR; cin >> n >> k >> PR; // f[i][j]: 用j个在[0,2n]范围内的数凑出和为i的方案数 int f[300000][20] = {0}; f[0][0] = 1; for (int i = 1; i <= (n + 1) * k; i++) { for (int j = 1; j <= k && j <= i; j++) { // 基础递推 f[i][j] = (f[i - j][j] + f[i - j][j - 1]) % PR; // 减去超出范围的情况 if (i >= (n + 1) * 2) { f[i][j] = (f[i][j] - f[i - (n + 1) * 2][j - 1]) % PR; } } } // 处理负数取模 int result = (f[(n + 1) * k][k] + PR) % PR; cout << result << endl; } return 0; }代码说明
主要变量:
n:尺子半长,总刻度数为k:要拿走的橡皮数量PR:模数f[i][j]:动态规划数组
算法步骤:
- 初始化
f[0][0] = 1 - 遍历所有可能的和
i和数量j - 应用递推公式计算方案数
- 处理负数取模,输出结果
关键点:
- 通过坐标平移将问题转化为非负整数划分
- 递推时及时减去超出范围的情况
- 最终答案为
f[(n+1)*k][k],对应原问题中和为0的情况
复杂度分析
- 时间复杂度:
- 外层循环: 次
- 内层循环:最多 次
- 空间复杂度:
- 1
信息
- ID
- 3698
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- 10
- 标签
- 递交数
- 7
- 已通过
- 1
- 上传者