射击运动员打靶

来源:互联网 发布:房地产经纪人网络教育 编辑:程序博客网 时间:2024/04/28 18:43

偶尔看到个题,一个射击运动员打靶,靶一共有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了,不能再放到一枪中了,上述公式不再适用……