蓝桥杯第7届javaA组决赛第二题
题目
把0~9这10个数字,分成多个组,每个组恰好是一个平方数,这是能够办到的。
比如:0, 36, 5948721
再比如:
1098524736
1, 25, 6390784
0, 4, 289, 15376
等等...
注意,0可以作为独立的数字,但不能作为多位数字的开始。
分组时,必须用完所有的数字,不能重复,不能遗漏。
如果不计较小组内数据的先后顺序,请问有多少种不同的分组方案?
注意:需要提交的是一个整数,不要填写多余内容。
思路:
这道题从结果上来推,先把10位以内所有的平方数算出来要用到大整数,否则会溢出,然后对这600多个数
进行全排列,并且在排列过程中进行减支减支1:记个分组组合在一起的字符串的字符个数不能超过10个
减支2:剩下的可用长度比当前选择字符串长度还小后面就不用看了,因为我们生成平方数从小到大生成的
后面的数肯定比当前的大(这个减支不必须只是会大大降低时间)
减支3:当前选的数不能和前面已经选的字符串有相同字符
程序设计思路:
首先用一个ArrayList<BigInteger> 数组来保存所有10为以内的平方数,然后接着用一个boolen[] select数组保存ArrayList<BigInteger> digitList中每一个数据是否被遍历过的状态遍历过则用true表示,没遍历过则用false
表示,
然后要检查当前字符串是否有相同数字,基本思想是,通过使用集合查重,因为集合中每一个元素都不相同
,把字符串当成一个set<Character>集合通过把字符串中的字符一个一传进集合中,如果集合的长度和字符串
的长度相同,没有重复,返回ture,否则字符串中有字符重复,返回false
接着用一个Set<Set<String>>集合来保存满足题意的所有分组,同时因为不考虑小组内数据的先后顺序
所以也可以使用集合来查重
再之后就是dfs深度遍历采用递归的方法,首先适合本题的dfs()函数需要三个参数int len,String figure,Set<String> figureset
每个参数的作用,第一个参数len用来表示分组中剩余的可添加字符串的长度,
第二个参数figure用来判断添加新字符串后分组是否有重复的字符
第三个参数figureset用来保存遍历到当前字符串后分组的集合
dfs()函数中的步骤,首先要判断len<0是return,否则判断len=0是说明分组已完成,添加到结果集中
return
然后进行一个for循环对于ArrayList<BigInteger> 中的每一个数开头的所有可以组成的分组,对for循环下首先是大判断
判断,判断该数是否被遍历过,没有遍历过将select[i]置为true然后将digitList[i]转化为字符串,判断 字符串的长度
是否大于剩余长度,若大于剩余长度则则不能将次平方数加入到集合中,再次将select[i]置为false跳出循环break
然后判断新加入的平房数和前面的数字是否有重复,若是没有重复则该字符串可以加入到该分组中,新建一个HashSet<Strign>
集合,然后将原先的figureset加入到集合中,然后再加入temp,递归深度遍历dfs(len-temp.length(),figure+temp,tempset)
最后就是写主函数,ArrayList<BigInteger> new对象,然后由小到大生成平方数使用一个while(true)循环,
初始的dfs(10,"",new HashSet<String>())
基本到此为止就是写代码的全部过程,还是比较不容易想得到的
接下来送上代码
package SevenProvience.test2;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class SelPow {
//声明一个数组链表用来存储所有的从小到大的平方数,要用
//大数类型,否则会溢出
static ArrayList<BigInteger> digitlist;
//对于数组中每一个元素都要标记状态,标记是否被遍历过
public static boolean[] select;
//判断当前字符串是否是否存在相同数字
//此处判断字符串是否相同是利用集合的特性,集合中元素都不相同
public static boolean checkDup(String digitStr){
int len=digitStr.length();
Set<Character> set=new HashSet<>();
for(int i=0;i<len;i++){
//charAt(intdex i)找出字符串中第i个字符
set.add(digitStr.charAt(i));
}
//判断集合的size是否和字符串的长度相同
//若相同说明没有重复
if(set.size()==len){
return true;
}else{
return false;
}
}
//用一个集合来保存所有的分组,因为分组里面是字符串,
//所以集合中嵌套集合
public static Set<Set<String>> result=new HashSet<>();
//进行深度遍历找出所有符合题目的分组并将分组放进result中
public static void dfs(int len,String figure,Set<String> figureset){
if(len<0){
return;
}
//判断剩余可添加的字符串长度是是否为0
//若为0说明分组已完成
if(len==0){
result.add(figureset);
return;
}
//使用for循环对figure中每一个数开头的情况下所有满足题意的分组
for(int i=0;i<select.length;i++){
//对于还没有遍历的平方数将select[i]置为false
if(!select[i]){
select[i]=true;
String temp=digitlist.get(i).toString();
//因为最先所有的平方数最先都是从小到大生成的,后面的数肯定比当前的数
//当前要长,因此后面的数就不用看了
if(len<temp.length()){
select[i]=false;
break;
}
//检查加入新平方数后是否存在重复
if(checkDup(figure+temp)){
HashSet<String> tempSet=new HashSet<>();
tempSet.addAll(figureset);
tempSet.add(temp);
//递归遍历
dfs(len-temp.length(),figure+temp,tempSet);
}
//因为对于循环的下一个dfs遍历还得用select[]数组所以要将select[i]
//置为false
select[i]=false;
}
}
}
public static void main(String[] args) {
int i = 0;
digitlist = new ArrayList<>();
//由小到大生成平方数
while (true) {
BigInteger temp = BigInteger.valueOf(i++).pow(2);
if (temp.toString().length() <= 10) {
if (checkDup(temp.toString())){
digitlist.add(temp);
System.out.println(temp.toString());
}
} else{
break;
}
}
select=new boolean[digitlist.size()];
dfs(10,"",new HashSet<String>());
//输出最终计算出的结果数目
System.out.println(result.size());
}
}