传球游戏问题

题目:

上体育课时,墨老师经常带着同学们一起做游戏。这次,墨老师带着同学们一起做传球游戏,游戏规则是这样的:个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
  聪明的张琪曼提出一个有趣的问题:有多少种不同的传球方法可以使得从张琪曼手里开始传的球,传了M次以后,又回到张琪曼手里。两种传球的方法被称作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并假设张琪曼为1号,球传了3次回到张琪曼手里的方式有1→2→3→1和1→3→2→1,共两种。

输入

有两个用空格隔开的整数N,M(3≤N≤30,1≤M≤30)。

输出

有一个整数,表示符合题目的方法数。

样例输入 复制
3 3
样例输出 复制
2

解决方案

递归+剪纸+记忆化搜索

暴力搜索(时间复杂度过高):

最开始的想法,直接暴力搜索,但是超时严重,只过了不到一半的点。

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

int n,m,ans;

void f(int d,int x){
	if(x < min(d, n - d)){
		return ;
	}
	if(x == 0){
		if(d == 0){
			ans ++;
		}
		return ;
	}
	f((d + 1) % n, x - 1) ;
	f((d + n - 1) % n, x - 1);
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin >> n >> m;
	f(0, m);
	cout << ans << '\n';
	return 0;
} 
 优化后
#include<bits/stdc++.h>
using namespace std;

int n,m,ans;

int p[35][35];//将递归过程存储下来

int f(int d,int x){
	if(p[d][x] != -1){
		return p[d][x];
	}
	if(x < min(d, n - d)){//在剩余传球次数不足以传回给张琪曼时进行剪纸
		return 0;
	}
	if(x == 0){// 当传球次数 x 为 0 时停止,判断此时球是否在张琪曼手中
		if(d == 0){
			return 1;
		}
		return 0;
	}
	p[d][x] = f((d + 1) % n, x - 1) + f((d + n - 1) % n, x - 1); // 将向左穿和向右传加起来
	return p[d][x];
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	memset(p,-1,sizeof p);
	cin >> n >> m;
	ans = f(0,m);
	cout << ans << '\n';
	return 0;
} 

动态规划

用空间换时间。

问题类型

在第 j 个位置的球只能由 j - 1 或 j + 1 的位置传过来,问题说要方案数,那么第 j 个位置时,已经传了m次的方案数 = 在 j - 1 的位置和 j + 1 的位置已经传了 m - 1 次的方案数之和。于是

定义状态

dp[i][j] 表示在传球 i 次后,在位置 j 的方案数。

转移方程
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1]
注意

在传球队伍头部位置和传球队伍尾部位置需要特殊处理

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

int dp[35][35];

int main(){
	int n,m;cin >> n >> m;
	dp[0][1] = 1;// 初始化
	for(int i = 1;i <= m;i ++){
		dp[i][1] = dp[i - 1][n] + dp[i - 1][2]; // 头部特殊处理 
		for(int j = 2;j <= n - 1;j ++){
			dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
		}
		dp[i][n] = dp[i - 1][n - 1] + dp[i - 1][1]; // 尾部特殊处理
	}
	cout << dp[m][1] << '\n'; // 传球 m 次后在第一个位置的方案数
	return 0;
}

结束! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值