【算法】Java蓝桥杯试题练习 算法训练组 《数字游戏》(回溯法)

关于蓝桥杯官网的算法训练组中的一道题目《数字游戏》的Java的回溯解法。

题目链接

蓝桥杯算法训练 <数字游戏>

题目:

试题 算法训练 数字游戏

资源限制

内存限制:256.0MB   C/C++时间限制:1.0s   Java时间限制:3.0s   Python时间限制:5.0s


问题描述

  给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
  例如:
  3 1 2 4
  4 3 6
  7 9
  16
  现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。


输入格式

  第1行为两个正整数n,sum


输出格式

  一个1~N的一个排列


样例输入

4 16


样例输出

3 1 2 4


数据规模和约定

  0<n<=10


分析题目:

        输入: 一个n,代表1~N的排列  &&  一个sum

        我们读完题目,大概可以知道1~N 的排列中,每次将相邻两个数相加,得到新序列,最后剩下一个数等于sum的话,就输出这个排列,且要求多种排列存在时输出字典序最小的排列。

        输出:字典序最小的符合条件的排列

Solution:

        既然这么明显了,我们可以

  1. 直接进行全排列,然后判断当前排列是否符合条件,存进一个容器里面。再筛选出字典序最小的进行输出就可以了。
  2. 优化一下,在进行全排列的时候用回溯变成符合字典序的全排列,判断当前排列是否符合条件,符合直接return回来,进行输出。 这样的话不需要进行完整的全排列,复杂度有所优化。

       

题解

 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Scanner;
 
 public class real {
 	public static void main(String[] args) {
 		Scanner sc = new Scanner(System.in);
 		int N = sc.nextInt();
 		int sum = sc.nextInt();
 		int[] nums = new int[N];
 		for (int i = 0; i < nums.length; i++) {
 			nums[i]= i+1; 
 		}
 		ArrayList<Integer> res = new ArrayList<Integer>();
 		ArrayList<ArrayList<Integer>> R = new ArrayList<ArrayList<Integer>>();
 		boolean[] used = new boolean[N];
 		permutation(nums,res,used,R,sum);
 		
 		for (int i = 0; i < R.get(0).size(); i++) {
 			System.out.print(R.get(0).get(i)+" ");
 		}
 	}
 	
 	static boolean isTrue(ArrayList<Integer> arr,int sum) {
 		ArrayList<Integer> initArrayList = new ArrayList<Integer>(arr);
 		while(initArrayList.size() != 1) {
 			for (int i = 0; i < initArrayList.size()-1; i++) {
 				int x = initArrayList.get(i)+initArrayList.get(i+1);
 				initArrayList.set(i, x);
 			}
 			if(initArrayList.size() != 1) {
 				initArrayList.remove(initArrayList.size()-1);
 			}
 		}
 		
 		if(initArrayList.get(0) == sum) return true;
 		else 
 			return false;
 		
 	}
 	
 	static void permutation(int[] nums,ArrayList<Integer> res,boolean[] used,ArrayList<ArrayList<Integer>> R,int sum) {
 		if(R.size() != 0) return;
 		if(res.size() == nums.length) {
 			if(isTrue(res, sum))
 				R.add(new ArrayList<Integer>(res));
 		}
 		for (int i = 0; i < nums.length; i++) {
 			if(!used[i]) {
 				used[i] = true;
 				res.add(nums[i]);
 				
 				permutation(nums, res, used, R, sum);
 				res.remove(res.size()-1);
 				used[i] = false; 
 			}
 		}
 		
 		
 	
 	}
 	
 	
//	 //交换法得到并不是字典序
// 	static void swap(int[] nums,int i,int j) {
// 		int temp = nums[i];
// 		nums[i]= nums[j]; 
// 		nums[j]= temp; 
// 	}
 	
 }



注意事项:

  1.  交换法得到的结果不是字典序排列的,所以不一定返回的是真正的字典序最小的结果。
  2.  在进行判断的函数中,注意不要改变容器本身的元素,在方法中新建一个克隆的,操作克隆的容器而不是操作原容器。
  3.  全排列函数中  ->R.add(new ArrayList<Integer>(res)); 注意容器要这样操作元素才能通过形参改变实参。因为容器是对象,对象是存放在堆区,所以要new一个再存储,否则一直都是操作同一个地址。
  4.  注意输出格式

but 还有一个测试点不知道是被什么卡到了、 拿到90分

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值