第七届 蓝桥杯决赛 Java B组 打靶 解题报告(DFS,回溯,全排列)

题目:

打靶

小明参加X星球的打靶比赛。
比赛使用电子感应计分系统。其中有一局,小明得了96分。


这局小明共打了6发子弹,没有脱靶。
但望远镜看过去,只有3个弹孔。
显然,有些子弹准确地穿过了前边的弹孔。

不同环数得分是这样设置的:
1,2,3,5,10,20,25,50

那么小明的6发子弹得分都是多少呢?有哪些可能情况呢?

下面的程序解决了这个问题。
仔细阅读分析代码,填写划线部分缺失的内容。

public class Main
{	
	static void f(int[] ta, int[] da, int k, int ho, int bu, int sc)
	{
		if(ho<0 || bu<0 || sc<0) return;
		if(k==ta.length){
			if(ho>0 || bu>0 || sc>0) return;
			for(int i=0; i<da.length; i++){
				for(int j=0; j<da[i]; j++) 
					System.out.print(ta[i] + " ");
			}
			System.out.println();
			return;
		}
		
		for(int i=0; i<=bu; i++){
			da[k] = i;
			f(ta, da, k+1,  __________________ , bu-i, sc-ta[k]*i);   // 填空位置
		}
		
		da[k] = 0;
	}
	
	public static void main(String[] args)
	{
		int[] ta = {1,2,3,5,10,20,25,50};
		int[] da = new int[8];
		f(ta, da, 0, 3, 6, 96);
	}
}
注意:只填写划线处缺少的内容,不要填写已有的代码或符号,也不要填写任何解释说明文字等。


本题结论有待验证,证明后更改,主要纠结于3代表总共三个弹孔,还是三次重复穿过弹孔

如果代表总共三个弹孔  答案:i > 0  ? ho - 1: ho

如果代表总共三次重复穿过:答案:i > 1 ? ho - (i - 1) : ho

分析:

1.main函数分析:

	public static void main(String[] args) {
		int[] ta = { 1, 2, 3, 5, 10, 20, 25, 50 };//记录分值
		int[] da = new int[8];//记录每个分值的个数
		 f(ta, da, k,ho,bu, sc);
		f(ta, da, 0, 3, 6, 96);//第一二个参数不用解释,从ta第0位开始枚举,3个重复弹孔,上限6个分数,共96分
	}

2.递归函数分析:

	static void f(int[] ta, int[] da, int k, int ho, int bu, int sc) {
		if (ho < 0 || bu < 0 || sc < 0)//最后ho bu sc 都大于0 才有递归的必要(剪枝) 
			return;
		if (k == ta.length) {// 当k枚举完ta数组(类似for循环的i),开始判断
			if (ho > 0 || bu > 0 || sc > 0)// 三个参数都等于0,说明递归过程会把已经枚举的值扣除相应的ho,bu,sc值
				return;
			for (int i = 0; i < da.length; i++) {//输出每个分值
				for (int j = 0; j < da[i]; j++)
					System.out.print(ta[i] + " ");
			}
			System.out.println();
			return;
		}

		for (int i = 0; i <= bu; i++) {//bu是分数个数的上限
			da[k] = i;//每一个分值从0~bu(即6)进行深搜枚举
			f(ta, da, k + 1, i > 1 ? ho - (i - 1) : ho, bu - i, sc - ta[k] * i); // 填空位置
		}
		/*刚开始直接填0,发现每个答案加起来就是96,唯一不同的就是,有的弹孔数不是3个
		 *可见,ho的值就是用来筛选的且要扣除有几个重复的,由da数组可知每个分值是记录每个分值个数的
		 *所以我推出ho,当分值的个数大于1,只要减去每个分值的个数扣掉1之后的值(即重复的数量),如da[1] = 3,那么我就ho扣掉2	
		 *最后运行,果然,得出了三组数据且只有三个弹孔,完美解决
		 * */
		da[k] = 0;//分值每种情况枚举完之后要回溯,清零
	}

把ho填0,得出的结果:


推出代码后结果:


所以应该填入: i > 1 ? ho - (i - 1) : ho

完整代码:

public class Main {
	static void f(int[] ta, int[] da, int k, int ho, int bu, int sc) {
		if (ho < 0 || bu < 0 || sc < 0)
			return;
		if (k == ta.length) {
			if (ho > 0 || bu > 0 || sc > 0)
				return;
			for (int i = 0; i < da.length; i++) {
				for (int j = 0; j < da[i]; j++)
					System.out.print(ta[i] + " ");
			}
			System.out.println();
			return;
		}

		for (int i = 0; i <= bu; i++) {
			da[k] = i;
			f(ta, da, k + 1, i > 1 ? ho - (i - 1) : ho, bu - i, sc - ta[k] * i); // 填空位置
		}

		da[k] = 0;
	}

	public static void main(String[] args) {
		int[] ta = { 1, 2, 3, 5, 10, 20, 25, 50 };
		int[] da = new int[8];
		 f(ta, da, k,ho,bu, sc);
		f(ta, da, 0, 3, 6, 96);
	}
}


总结:

主要还是考深搜还有回溯,跟全排列有点像,类似全排列的进阶

















评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值