环上传球计数:设 f(i,j) 为传 i 次后球在 j 手中的方案数,每步只能从左右邻居转移,答案 f(m,0),O(mn) 递推。
OJ: roj
题目 ID: 3562
难度:普及
标签:动态规划计数环
创建: 2026-10-02 07:48
更新: 2026-10-07 13:50
前置题目
leetcodecn climbing-stairs爬楼梯A 教的“某格方法数等于其前驱邻格方法数之和对并定初值”被 B 直接用作传球 DP:把 i-1、i-2 换成环上 (j-1)%n、(j+1)%n 即可luogu P1255数楼梯B 直接复用 A 教的「最后一步只有两种来源就相加」计数模板:把 dp[i]=dp[i-1]+dp[i-2] 换成环上左右邻居两个前驱相加,仅额外套一层 (j±1)%n 绕回,难度落在 A 之上两档。leetcodecn pascals-triangle杨辉三角B 的 DP 逐层计数直接沿用了 A 教的「本层每格 = 上一层两个相邻格子相加」这一步二维逐层递推,只把上一层下标加取模改成左右邻居并补了边界与环的处理
题目描述
输入格式:一行两个整数
输出格式:一个整数,表示方案数。
样例输入:3 3,样例输出:2。
思路
设
参考代码
Python 版:
python
import sys
from functools import cache
def solve(n: int, m: int) -> int:
"""f(i, j):传 i 次后球在 j 号手里的方案数,邻居环上取模。"""
@cache
def f(i: int, j: int) -> int:
if i == 0:
return j == 0 # 开始时球在 1 号(下标 0)手里
return f(i - 1, (j - 1) % n) + f(i - 1, (j + 1) % n) # 一次只能从左右邻居传来
return f(m, 0)
data = iter(map(int, sys.stdin.buffer.read().split()))
n, m = next(data), next(data)
print(solve(n, m))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 14:05
* update_at: 2026-10-06 14:05
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
// dp[i][j] 表示传 i 次后球在 j 号同学手中的方案数
ll dp[35][35];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
dp[0][0] = 1; // 初始球在小蛮(0 号)手中
for (ll i = 1; i <= m; i++) {
for (ll j = 0; j < n; j++) {
ll left = (j - 1 + n) % n; // j 的左邻居
ll right = (j + 1) % n; // j 的右邻居
dp[i][j] = dp[i - 1][left] + dp[i - 1][right];
}
}
cout << dp[m][0] << "\n";
return 0;
}