题解:洛谷 P1057 传球游戏

【题目来源】

洛谷:P1057 [NOIP 2008 普及组] 传球游戏 - 洛谷

【题目描述】

上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。

游戏规则是这样的:\(n\) 个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大家表演一个节目。

聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了 \(m\) 次以后,又回到小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有三个同学 \(1\) 号、\(2\) 号、\(3\) 号,并假设小蛮为 \(1\) 号,球传了 \(3\) 次回到小蛮手里的方式有 \(1→2→3→1\)\(1→3→2→1\),共 \(2\) 种。

【输入】

一行,有两个用空格隔开的整数 \(n,m(3≤n≤30,1≤m≤30)\)

【输出】

\(1\) 个整数,表示符合题意的方法数。

【输入样例】

3 3

【输出样例】

2

【核心思想】

  1. 问题分析\(n\) 个同学站成一个圆圈,小蛮(编号 \(1\))开始传球,每次可以传给左边或右边的相邻同学,传 \(m\) 次后求球回到小蛮手里的方案数。这是一个环形线性DP问题,关键在于用DP记录每一步到达每个位置的方案数,并用模运算处理环形结构。

  2. 算法选择

    • 线性DPf[i][j] 表示传 \(i\) 次后到达第 \(j\) 个同学的方案数
    • 环形处理:使用模运算 (j - 1 + n) % n(j + 1) % n 处理环形相邻关系
    • 状态转移:当前位置的方案数由上一轮左右相邻位置的方案数之和得到
  3. 关键步骤

    • 初始化f[0][0] = 1,表示传 \(0\) 次时球在小蛮(编号 \(0\))手里,方案数为 \(1\)
    • 状态转移:对于每一步 \(i\)\(1 \leq i \leq m\))和每个位置 \(j\)\(0 \leq j < n\)
      • f[i][j] = f[i-1][(j-1+n)%n] + f[i-1][(j+1)%n]
      • 即当前位置 \(j\) 的方案数等于上一轮左边和右边相邻位置的方案数之和
    • 答案获取f[m][0],即传 \(m\) 次后回到小蛮(编号 \(0\))手里的方案数
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times m)\),外层 \(m\) 步,内层 \(n\) 个位置
    • 空间复杂度:\(O(n \times m)\),二维DP数组(可优化为 \(O(n)\)
  5. 环形线性DP的核心思想

    • 状态设计f[i][j] 表示经过 \(i\) 步到达位置 \(j\) 的方案数
    • 环形处理:使用模运算将环形结构转化为线性结构处理,编号 \(0\)\(n-1\) 相邻
    • 无后效性:当前状态只与上一轮状态有关,满足DP最优子结构
    • 方案数DP:适用于"从起点出发,走 \(m\) 步回到起点的路径数"类问题
    • 可空间优化:因为只依赖上一轮状态,可用滚动数组将空间优化至 \(O(n)\)

【算法标签】

普及 #线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 35;      // 定义最大节点数
int n;                 // 节点总数(环形排列)
int k;                 // 移动步数
int f[N][N];           // DP数组,f[i][j]表示移动i步后到达j位置的方案数

int main()
{
    // 输入节点数和移动步数
    cin >> n >> k;

    // 初始化:0步时在0位置的方案数为1
    f[0][0] = 1;

    // 动态规划计算
    for (int i = 1; i <= k; i++)        // 遍历每一步
    {
        for (int j = 0; j < n; j++)     // 遍历每个位置
        {
            // 状态转移:当前步数i到达j的方案数等于
            // 上一步i-1在j-1位置的方案数加上i-1在j+1位置的方案数
            // 使用模运算处理环形结构
            f[i][j] = f[i - 1][(j - 1 + n) % n] + f[i - 1][(j + 1) % n];
        }
    }

    // 输出移动k步后回到原点的方案数
    cout << f[k][0] << endl;

    return 0;
}

【运行结果】

3 3
2
posted @ 2026-06-25 19:14  团爸讲算法  阅读(13)  评论(0)    收藏  举报