Java题解—1005 继续(3n+1)猜想
原题
当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n=3 进行验证的时候,我们需要计算 3、5、8、4、2、1,则当我们对 n=5、8、4、2 进行验证的时候,就可以直接判定卡拉兹猜想的真伪,而不需要重复计算,因为这 4 个数已经在验证3的时候遇到过了,我们称 5、8、4、2 是被 3“覆盖”的数。我们称一个数列中的某个数 n 为“关键数”,如果 n 不能被数列中的其他数字所覆盖。
现在给定一系列待验证的数字,我们只需要验证其中的几个关键数,就可以不必再重复验证余下的数字。你的任务就是找出这些关键数字,并按从大到小的顺序输出它们。
输入格式:
每个测试输入包含 1 个测试用例,第 1 行给出一个正整数 K (<100),第 2 行给出 K 个互不相同的待验证的正整数 n (1<n≤100)的值,数字间用空格隔开。
6
3 5 6 7 8 11
结尾无空行
输出格式:
每个测试用例的输出占一行,按从大到小的顺序输出关键数字。数字间用 1 个空格隔开,但一行中最后一个数字后没有空格。
7 6
结尾无空行
解读
- 关键数n要满足什么条件?
除开数字自身,它重复计算偶数n\2或奇数(3n+1)\2时生成的一个数列,而待测验的一组数不在这个数列中。
1、反向移除不是关键数的:
反过来求,不是关键数n的(关键数要满足所有数字的“数列”都核对一遍,不是关键数只要在一个“数列”里就可以了)。
只要带测验的数,在任何一个“数列”中,就把它移除。这样剩下的就是关键数了。
这里我用列表来remove(num),不需数组的遍历移除
2、正向找:
把测试里的每个数生成的除开自身的数放一个集合里,再检验测试中的那些数字不在这个集合里,即该数字是关键数
代码
import java.util.*;
/**
* 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半;
* 如果它是奇数,那么把 (3n+1) 砍掉一半。这样一直反复砍下去,最后一定在某一步得到 n=1
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = Integer.parseInt(scanner.nextLine());
String str=scanner.nextLine();
//把测试样例存入列表
String[] arr = str.split(" ");
// List<String> list = Arrays.asList(arr);//这样生成的集合,只能读取,不能修改
List<Integer> arrayList = new ArrayList<>();
for(int j=0;j<arr.length;j++){
arrayList.add(Integer.parseInt(arr[j]));
}
//把生成的数列里的每个数字,检验是否在测验样例中。如果在就移除
for (int i=0;i<n;i++){
int num=Integer.parseInt(arr[i]);//要用arr里的元素,因为list中的删除元素,长度变小arrayList.get(i)会报索引越界错误
while(num!=1){
if (num%2==0) {
num = num / 2;
if (arrayList.contains(new Integer(num))){
arrayList.remove(new Integer(num));
}
}
else {
num = (num * 3 + 1) / 2;
//把int转换成包装型,是因为List中的remove(int index)会与Collection中的remove(Object o)方法冲突
if (arrayList.contains(new Integer(num))){
arrayList.remove(new Integer(num));
}
}
}
}
//排序输出
arrayList.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return -Integer.compare(o1,o2);
}
});
for (int i=0;i<arrayList.size();i++){
if (i<arrayList.size()-1){
System.out.print(arrayList.get(i)+" ");
}else
System.out.print(arrayList.get(i));
}
}
}
C++代码正向求解
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
int flag[10000];
int main(){
int K,i,j,a[101],t;
scanf("%d",&K);
for(i=0;i<K;i++){
scanf("%d",&a[i]);//样例
}
memset(flag,0,sizeof(flag));
sort(a,a+K);//起始地址和结束地址
for(i=0;i<K;i++){
t=a[i];
while(t!=1){
if(t%2==0) t=t/2;
else t=(3*t+1)/2;
flag[t]=-1;//不能包括自身
}
}
for(j=K-1;j>=0;j--){
if(flag[a[j]]==0)
printf("%d ",a[j]);
}
return 0;
}