射击运动员打靶

偶尔看到个题,一个射击运动员打靶,靶一共有10环,连开10枪打中90环的可能性有多少种?要求用递归算法解决。

递归求解是很自然的一种思路:

m枪打中n环,s(m,n)对应其射击方案

对第一枪有0-10环10种选择,假定为k环,然后对剩下的m-1枪,很显然s(m-1,n-k)对应其射击方案

因此:

 

很容易写一个对应的Java程序,输出射击方案

 

import java.util.Arrays;

public class ShootingPlan {
	static int count = 0;
	static int numberOfShooting = 10;
	static int totalScore = 90;
	
	public static void main(String[] args) {
		
		
		//score数组表示一个射击计划,score[i]表示第i枪的环数
		int [] score = new int[numberOfShooting];
		Arrays.fill(score, 0);
		
		long beginTime = System.currentTimeMillis();
		shootplan(numberOfShooting, totalScore, score);
		long endTime = System.currentTimeMillis();
		
		System.out.println("Executed in " + (endTime - beginTime) + " milliseconds");
	}

	/**计算一个射击计划
	 * @param number 射击次数
	 * @param total  总环数
	 * @param score  记录成绩的数组 */
	private static void shootplan(int number, int total, int[] score) {
		// 边界条件,如果仅剩一枪且剩余环数符合条件,那剩余环数即为最后一枪的成绩,同时打印输出10枪成绩
		if(number == 1 && total <= 10 && total >= 0) {
			score[numberOfShooting - number] = total; // 输出一个射击计划 
			System.out.print(++count + ":");
			for(int i : score) 
				System.out.print(i + " ");
			
			System.out.println();
		}
		
		if(number > 1) {
			for(int i = 0; i <= 10; i++) {
				score[numberOfShooting - number] = i;
				shootplan(number - 1, total - i, score);
			}	
		}
	}

}



运行结果最后几行如下

 当然,上面这个实现谈不上什么效率,是指数级的算法:(

可以看到执行时间是18.483秒

 

尝试优化算法

1.考虑减小搜索空间——在递归计算中,当S(i,k)的k>10*i时,可以终止搜索,因为成绩超出上限,枪枪靶心也game over……

改进后的方法及运行结果

	/**计算一个射击计划
	 * @param number 射击次数
	 * @param total  总环数
	 * @param score  记录成绩的数组 */
	private static void shootplan(int number, int total, int[] score) {
		// 边界条件,如果仅剩一枪且剩余环数符合条件,那剩余环数即为最后一枪的成绩,同时打印输出10枪成绩
		if(number == 1 && total <= 10 && total >= 0) {
			score[numberOfShooting - number] = total; // 输出一个射击计划 
			System.out.print(++count + ":");
			for(int i : score) 
				System.out.print(i + " ");
			
			System.out.println();
		}

		// 减小搜索空间
		if(total > number * 10)
			return;
		
		if(number > 1) {
			for(int i = 0; i <= 10; i++) {
				score[numberOfShooting - number] = i;
				shootplan(number - 1, total - i, score);
			}	
		}
	}


还是有明显差异的

 

2.是否可以借鉴动态规划算法,存储计算过的子结构以空间换时间?

感觉上是靠谱的,但这个空间开销貌似有点惊艳…… 找机会试试

 

下面重头戏,能不能直接计算出方案总数? (不战而屈人之兵自然爽,总不能每次都傻傻跑段代码给出结果)

 

重新整理下需求——10枪打90环的方案数

 

 

用组合数学里的一一对应思想转化下,考虑补集——每枪少打的环数xi'

 

即关注每一枪比10环少的环数,10枪打90环 等价于10枪共少打10环,那么每枪少几环的方案数和原方案数一一对应

把总共少打的10环分配到10枪中,相当于把10个球放到10个不同的盒子里,盒子可以为空

 

熟悉组合数学的朋友可以一眼看出,这是允许重复的组合,结果数为C(10+10-1,10) = C(19,10) = 92378

 

或者用隔板法考虑,把10个球放到10个不同的盒子里,盒子可以为空,等价于把10个球分成10堆

 

需要9个隔板,把球和隔板的位置一字排开,结果数为C(19,10)或者C(19,9) = 92378

 

 

进一步考虑,m枪打n环,如果 n>= 10m - 10,那么总的方案数为 C(10m-n+9, 9)

 

如果n< 10m - 10,这时候miss的环数大于10了,不能再放到一枪中了,上述公式不再适用……

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值