传球游戏:一道有趣(普及)的DP。
2008年普及组T3,值得一刷!(大雾
题目描述
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的: 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个整数,表示符合题意的方法数。
解法:
分析整道题:
本题所求的是有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了 m 次以后,又回到小蛮手里。
并且我们可以获得题面中的隐含条件:两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。
显然,这道题可以用DP来解决。
设一个二维数组f[x][y]代表传了y次后到达了x个人的方案数。设f[i][j]为当前的状态,那么上一个状态可以认为是f[i-1][j-1]或者是f[i+1][j-1]
为什么?
因为y代表次数,从上一个状态转移到这个状态代表次数+1,而这个球可以从左边或者右边的同学传过来,所以i-1代表一个方向,而i+1代表另一个方向
所有的状态转移方程为f[i][j]=f[i-1][j-1]+f[i+1][j-1];
但是这个题还没有结束。
先考虑第一个人,可以从第n个人转移过来,也可以从第二个人转移过来。f[1][i]是初始状态,代表第一个人传多少次的方案数,所以可得f[1][j]=f[2][j-1]+f[n][j-1];
再考虑一下第n个人。
同理,n可以从第一个人或者第n-1个人转移过来,那么代表f[n][j](传了i次到达第n个人的方案数)=f[n-1][j-1]+f[1][j-1];
本题就解完了!
外层变量为j,内层变量为i,因为有f[1]和f[n]需要特判。
C++:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<string> #include<map> #include<set> #include<queue> #define INF 0x3f3f3f3f #define inf 0x3f #define ll long long using namespace std; int f[31][31],n,m; int main(){//P1057重构代码 cin>>n>>m; f[1][0]=1; for(int j=1;j<=m;j++){ for(int i=2;i<=n-1;i++){ f[i][j]=f[i-1][j-1]+f[i+1][j-1]; f[1][j]=f[n][j-1]+f[2][j-1]; f[n][j]=f[1][j-1]+f[n-1][j-1]; } } cout<<f[1][m]; return 0; }