几个注意点:
①区分清楚二维数组三行分别的作用
②强化熟悉冒泡排序和选择排序!
下面是我自己最初的解法
#include<stdio.h>
int main()
{
int n,i,j,k,max,cnt;
int s,p,q,w;
int a[3][100]; //0、1、2分别为原始值,校验值,修改后的值
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a[2][i]);
a[0][i]=a[2][i];
a[1][i]=1;
}
for(i=0;i<n;i++)
{
if(a[1][i])
{
while(a[2][i]!=1)
{
if(a[2][i]%2==0)
{
a[2][i]=a[2][i]/2;
}
else
{
a[2][i]=(3*a[2][i]+1)/2;
}
for(j=0;j<n;j++)
{
if(a[0][j]==a[2][i])
{
a[1][j]=0;
}
}
}
}
}
for(i=0;i<n;i++)
{
if(a[1][i])
{
a[0][cnt]=a[0][i];
cnt++;
}
// printf("cnt=%d ",cnt);
// printf("a=%d ",a[0][i]);
}
//排序开始
for(q=0;q<cnt-1;q++)
{
max=q;
for(k=q+1;k<cnt;k++)
{
if(a[0][k]>a[0][max])
{
max=k;
}
}
s=a[0][max];
a[0][max]=a[0][q];
a[0][q]=s;
} //选择排序
//下面是另一种:冒泡排序
// for (q = 0; q < cnt - 1; q++)
// for (k = 0; k < cnt - 1 - q; k++)
// {
// if(a[0][k] < a[0][k+ 1])
// {
// s = a[0][k];
// a[0][k] = a[0][k + 1];
// a[0][k+ 1] =s;
// }
// } //冒泡排序结束
//排序结束 开始输出
for(w=0;w<cnt-1;w++)
{
printf("%d ",a[0][w]);
}
printf("%d",a[0][cnt-1]);
}
下面是搜集到的其他解法:
#python3,
if __name__ == "__main__":
n = int(input())
num_list = input().split()
a = set() # 所有遍历过的数字的集合
res = [] # 关键数的列表
for num in num_list:
num = int(num)
if num not in a: # 是集合中未出现的数字,添加到关键数,并进行3n+1的计算;否则不予理会
res.append(num)
r = num
while r!=1 and r not in a: # 进行计算,将途中出现的数字添加到集合中,直到结果为1或覆盖到集合中某数(不一定为关键数)
a.add(int(r))
if r % 2 == 1:
r = (3*r+1)/2
else:
r = r/2
if r in res: # 如果覆盖到了关键数,删除它
res.remove(r)
res.sort(reverse=1) # 进行排序处理并输出,老生常谈的步骤
for num in res:
if num ==res[-1]:
print(num)
break
print(num,end=" ")
另外一种c语言思路可供借鉴
博主的错误思路:
开始时,我想建立一个数组,每输入一个数字,就存储进去一个。然后对数组的每一个成员进行操作,判断它是否满足猜想,每判断一次,就把判断过程中的n
= n/2后的n拿出来,与数组中的各个成员比较,出现过,就把它从数组中除去,变为零(而且怎么除去也是一个问题,然后我就想到用链表,miaomiaomiao?越来越偏)。把所有的数都操作一次后,在把数组中剩下的数排序,输出。根本不用打,这么麻烦,很明显超时了。综合网上答案整理的思路:
用标记。
建立一个大小105的数组为a[105](这是为了避免数组越界问题),把这个数组用mamset函数赋值,都变成一个-1,这让我想起了有的时候程序里面的flog变量,
用这个标记来控制程序。为什么所有变量都变为-1呢?逗你呢,其实设成什么初值都
一样的。
读入第一个数,拿到要输入的数的个数。第二行就可以输入了。但并不是把数直
接存入数组,而是进行标记。读入某个数,就把那个位置修改一下。比如读入i,就把
a[i]里面存的数修改一下,改为0。以后,只要是某位置i里面存的数是0,就意为你输
入过i。
都读完后,就可以开始处理这组数据了。处理的规则已经给你了,就是按照猜想。
假设你要处理的数是n。
1.首先,我要找到n。从头到尾处理,n是你输入的第一个数。只要是你输入过的
也需要处理的数,那么以它为下标的数组a[n]里面存的值是0。根据这个规则,你可以拿到值n。
2.对它进行处理,根据规则,每次处理后,n = n / 2,根据题意,这是题目要求
的,被n覆盖的数,自然知道这个数不必再次处理。
那么问题来了,怎么区分被覆盖的数和其他暂时未被覆盖的数(这里面可能有现
在还未处理到的被覆盖的数和关键数)。用标记,把被覆盖的数变成除-1,0,之外的数
就好了啊!
3.把它变为1。
4不断循环该步骤1,2,3,把整个数组都循环完毕。
这道题做出来了,-1代表你没输入过的数,与题目无关的数;1代表被覆盖的数;
0呢?自然是两者之外的,即是输入过而且也没被覆盖过的数!那么就是我们要的,未
知数咯!
只需从后往前遍历,把所有里面是0的位置的下标输出就好了(a[i] == 0吗?
等于的话输出i)
恰好满足了题目从小到大排序打要求。当然我从小到大也行啊,从前往后就好了。
而且题目要求的输出格式要注意一下,即每个数之间要有空格,并且最后一个数后面没
有空格。
#include<stdio.h>
#include<string.h>
int main()
{
int a[105];
memset(a,-1,sizeof(a));
int sizearray;
scanf("%d",&sizearray);
int num;
int i;
for(i = 0;i < sizearray;i++)
{
scanf("%d",&num);
a[num] = 0;
}
int n;
for(i = 1;i <105 ;i++)
{
if(a[i] != 0)
continue;
n = i;
while(n != 1)
{
if(n % 2 == 0)
{
n = n/2;
if(n<100){
a[n] = 1;
}
}
else
{
n = 3*n + 1;
n = n/2;
if(n<100)
{
a[n] = 1;
}
}
}
}
for(i = 104;i > 0;i--)
{
if(a[i] == 0)
{
printf("%d",i);
break;
}
}
for(i = i-1; i > 0;i--)
{
if(a[i] == 0)
{
printf(" %d",i);
}
}
}
另一个思路:
题目理解: 这道题主要是对题意得分析,搞清楚“关键数”的定义, 就是该数不会出现在判断数组中某个数是否满足卡拉兹猜想的过程中。
搞清楚这个,那接下来就简单了。 首先找出判断某个数满足猜想过程中出现的所有数 然后让原数组中的数和上一步出现的所有数一一比较,
只要数组中出现相同匹配的数就想办法把这个数从数组剔除, 最后排序输出就行
#include<stdio.h>
#include<malloc.h>
void sort(int *,int );
int main()
{
int *a;
int n,i,t,j;
scanf("%d",&n);
a=(int*)malloc(n*sizeof(int)); // 动态申请数组空间
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(i=0;i<n;i++)
{
t=a[i];
if(t==0)
continue;//碰到数组中有0的项,则跳过
while(t!=1)
{
if(t%2==0)
t/=2;
else
t=(3*t+1)/2;
for(j=0;j<n;j++)//数组中所有数与运算中出现的数比较
{
if(a[j]==t)//如果数组中有和运算过程中相同的数,就把数组中的该数改为0
{
a[j]=0;
break;//因为输入的数组是互不相同的数,所以不会存在有多个匹配的情况,找到有一个匹配就可跳出,以节省内存减少运行时间。
}
}
}
}
sort(a,n);//排序
for(i=0;a[i]>0;i++)
{
printf("%d%s",a[i],a[i+1]>0?" ":"");
}
}
void sort(int *P,int K) //排序
{
int i,j,temp;
for(i=0;i<K;i++)
for(j=i+1;j<K;j++)
{
if(P[i]<P[j])
{
temp=P[i];
P[i]=P[j];
P[j]=temp;
}
}
}
java实现:
解题思路是:创建两个数组arr,第一个为输入数组b,第二个为局部数组,先遍历输入数组的数组,然后计算出该数的所以重复数字,把这些重复数字归入到一个数组中,再遍历数组,把测试数据中与该数组相同的数字归零(由于题目给出n不可能小于零,所以我归零做标记),最后的得到的
数组arr不为零的数则为我们需要的数字,再加个判断加个排序就可以得到答案。
诶这题刚开始想用C语言做的(毕竟PAT推崇C语言),但是发现一些困难,首先我不知道怎么用函数来返回一个数组变量,其次c语言我不知道怎么用方法来对数组进行排序(这也是我对c语言不大熟悉的原因),于是我转用熟悉的java,十分钟之内搞定
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] a = new int [n];
for(int i =0 ;i<n;i++){
a[i] = in.nextInt();
}
for (int i = 0; i < n; i++) {
if(a[i]!=0){
ArrayList<Integer> b= new ArrayList();
b= cut(a[i],b);
for(int bb:b){
for(int j = 0;j<n;j++){
if(a[j]==bb){
a[j]=0;
}
}
}
}
}
Arrays.sort(a);
for (int i = n-1; i >0; i--) {
if(a[i]!=0){
if(i==n-1)
System.out.print(a[i]);
else
System.out.print(" "+a[i]);
}
}
}
private static ArrayList<Integer> cut(int num, ArrayList<Integer> b) {
if (num == 1) {
b.add(num);
return b;
}
if (num % 2 == 0) {
num = num / 2;
b.add(num);
cut(num, b);
}
else {
num = (num * 3 + 1) / 2;
b.add(num);
cut(num, b);
}
return b;
}
}
另一个博主的c语言实现:
思路 最简单的思路:
记录初始给出的关键数字,对每一个数字进行验证计算,每遇到初始给出的数字时,对其进行标记。标记过的数字便不再进行验证。
思路优化:
如果(也是一般)进行从小到大的遍历,验证一个较大数字时,某一步计算得到一个较小数字(并且在初始给出的数字中),即可断定无须继续验证,因为接下来的验证过程和这个较小数字的验证过程是重复的。
代码实现:
所给数字范围是1-100,可以使用长度为100(或101,方便数字和索引对应)的数组记录需验证的数字,数字即为索引。这样无须查找排序等操作。初始给出的数字相应记为1,其他置0。
验证过程中会遇到大于100的数,注意条件判断,避免数组越界。
#include <stdio.h>
int main()
{
int K, n;
int tabel[101] = {0};
scanf("%d", &K);
for(int i = 0; i < K; i++)
{
scanf("%d", &n);
tabel[n] = 1;
}
/* find numbers needed to test */
for(int i = 1; i <= 100; i++) if(tabel[i])
{
/* reuse variable n here */
for(n = i; n > 1; )
{
/* calculate one step */
if(n % 2) n = (3 * n + 1) / 2;
else n /= 2;
/* see if the new number is in given numbers */
if(n <= 100 && tabel[n])
{
tabel[n] = 0;
K--; /* one less number not 'covered' */
if(n < i) break; /* did this before, no need going on */
}
}
}
for(int i = 100; i >= 1; i--) if(tabel[i] == 1)
{
printf("%d%c", i, --K ? ' ' : '\0');
}
return 0;
}