CSP历年复赛题-P1057 [NOIP2008 普及组] 传球游戏
原题链接:https://www.luogu.com.cn/problem/P1057
题意解读:n个人围一圈,从1开始传球m次,每次可以往左或右传,计算球再次传给1的方案数。
解题思路:
求方案数,通常就是DP问题,此题DP状态并不难想,如果实在不会,也可以通过DFS暴搜得部分分。
1、DFS
60分代码:
#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
//经过cnt次传球到x
void dfs(int cnt, int x)
{
if(cnt == m)
{
if(x == 1) ans++;
return;
}
int next = x + 1; //下一次传给x+1
if(next > n) next = 1;
dfs(cnt + 1, next);
int pre = x - 1; //下一次传给x-1
if(pre < 1) pre = n;
dfs(cnt + 1, pre);
}
int main()
{
cin >> n >> m;
dfs(0, 1);
cout << ans;
return 0;
}
2、动态规划
状态表示:
设dp[i][j]表示经过i次球从1传到j的方案数
状态转移:
考虑i-1次的情况,i-1传球可以传到j-1,也可以传到j+1,
因此可以得到dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]
但是需要考虑j的取值,当j == 1时 j - 1替换为n,当j == n时,j + 1替换为1
初始化:
dp[0][1] = 1,0次传球到1的方案是1
结果:
dp[m][1],m次传球到1的方案数
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 35;
int n, m;
int dp[N][N]; //dp[i][j]表示经过i次球从1传到j的方案数
int main()
{
cin >> n >> m;
dp[0][1] = 1;
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
if(j == 1) dp[i][j] += dp[i-1][n];
else dp[i][j] += dp[i-1][j-1];
if(j == n) dp[i][j] += dp[i-1][1];
else dp[i][j] += dp[i-1][j+1];
}
}
cout << dp[m][1];
return 0;
}