题目链接(力扣网):https://leetcode-cn.com/leetbook/read/interesting-algorithm-puzzles-for-programmers/97r135/
1.一个蛋糕切成N块,每个蛋糕上的草莓个数为1~N,且不重复
2.相邻两块蛋糕上的草莓数之和,是平方数(1,4,9,16,...)
求:满足上述要求的最小N
1.让N从小往大遍历,找出第一个符合要求的就是最小值
2.集合SList用于保存平方数,如果相邻蛋糕草莓数之和存在于集合SList,则符合要求
3.集合NList用于保存1~N这N个数字,每选择一个数字就从集合中删除一个数字,NList集合删空时表示一种尝试已到尽头
4.每一步只需要知道上一步选择的数字、还有哪些数字可选、符合要求的平方数集合——递归
public static void main(String[] args) {
List nList = new LinkedList(); // 可用数字集合
List sList = new ArrayList(); // 平方数集合
for(int n = 1, m = 1; n < 500 ; n ++){
nList.add(n); // 可用数字1~n
if(m * m <= 2 * n - 1){ // 相邻蛋糕草莓数最大值:n + (n-1) == 2*n-1
sList.add(m * m); // 平方数(没必要无限大,够用即可)
m ++;
}
System.out.println(sList);
nList.remove(new Integer(1)); // 直接填数字,会被当作下标
if(cut(1,nList,sList)){ // 因为是个圆,所以从几开始都行。但一开始只有1块,所以此处从1开始。自定义方法cut
System.out.println("\nmin N = "+n);
break;
}
}
}
/**
* 切蛋糕
* @param pre 上一步选择的数字
* @param nList 剩余可选数字(1~N)
* @param sList 平方数集合
*/
public static boolean cut(int pre, List nList, List sList){
if(nList == null || sList == null) return false; // 入参检查
// 递归出口
if(nList.size() == 0){ // 决定了该条支路的最终成败,上层的递归的返回值都只是传递这个最终结果。如果这里成功了,则该条支路全都return true
if(sList.contains(pre + 1)){ // 因为是个圆,所以第一块从谁开始算都可以。假设从1开始。最后一块pre是否能和最开始的1拼成平方数。能拼成则成功
System.out.print(pre+",");
return true;
}
return false;
}
// 递归调用
for(int i = 0; i < nList.size(); i ++){ // 剩余可用数字,逐一尝试
int num = nList.get(i); // 本次选的数字
if(sList.contains(num + pre)){ // 能和上一块蛋糕的草莓数构成平方数
nList.remove(i); // 修改剩余的可用数字
if(cut(num,nList,sList)){
System.out.print(pre+",");
return true;
}else{
nList.add(i,num); // 恢复可用数字
}
}
}
return false; // 已经尝试了当前情况的所有后续可能
}
15,10,26,23,2,14,22,27,9,16,20,29,7,18,31,5,11,25,24,12,13,3,6,30,19,17,32,4,21,28,8,1,(倒着输出的)
min N = 32