1005 继续(3n+1)猜想 (25 分)
题目描述:
卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。
当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 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)的值,数字间用空格隔开。
输出格式:
每个测试用例的输出占一行,按从大到小的顺序输出关键数字。数字间用 1 个空格隔开,但一行中最后一个数字后没有空格。
输入样例:
6
3 5 6 7 8 11
输出样例:
7 6
解题思路:
题目要求一个列表中的关键数,而关键数的定义是 在列表中其他数字 “归一”的过程中产生的数字集合不包含其自身的数字。举个例子,[3, 6] ,3在 “归一”的过程中产生的中间数字有:“5,8,4,2,1”。而 6 并不包含在其中,所以6是一个关键数。而 3 不是,因为 6 “归一”的过程中产生了“3,5,8,4,2,1”,3是包含在其中的。
这样我们就需要在每个 可能是 关键数 的数字的“归一”过程中去否定那些不是关键数的其他数字。直到每个可能的关键数 都“归一”了,我们才能判断出哪些是关键数。
代码:
Python Version
def main():
n = int(input())
# 接收输入的整数,不过在本程序中并没有用到这个整数。
data = [int(x) for x in input().split(" ")]
# 接收将要判断的数字并存储为整数数组。
# print(data)
flag = [False for x in range(n)]
# 用一个数组来表示 data中下标相同数字是否是 关键数
for x in range(n):
if flag[x]:
# 这个数不是关键数
continue
else:
temp = data[x]
# 这个数还没有确定是否是关键数。也就是说,在前面所有数字 “归一” 的过程中还没有出现这个数字。
while temp != 1:
# 对这个数字“归一”
if temp % 2 == 0:
temp //= 2
else:
temp = (temp * 3 + 1) // 2
if temp in data:
# 如果在“归一”过程中,出现了列表中的其他数字,说明其他数字不是关键数
# 列表的index方法可以返回列表中某一项第一次出现的下标。由于列表中的数字互不相同,所以这样做没有问题。
# 将不是关键字 对应的flag数据标志为 True
flag[data.index(temp)] = True
# print(flag)
res = []
for x in range(n):
if flag[x] == False:
# 如果flag[x]是False,则对应的data[x]是关键数。
res.append(data[x])
res.sort(reverse=True)
# 列表的sort方法可以对列表排序,默认为从小到大,设置reverse = True获得从大到小的排序结果
res = [str(x) for x in res]
# 将 res 从数字列表 转换为 字符列表
print(" ".join(res))
# 使用join函数拼接出答案并输出
return
if __name__ == '__main__':
main()
C++ Version
#include <iostream>
#include <algorithm>
#include <vector>
#define NN 100 + 10
using namespace std;
bool in(int temp, int* data, int K);
int main(){
//freopen("in.txt", "r", stdin);
int K = 0, temp;
int data[NN];
int flag[NN];
for(int i=0; i<NN; ++i){
flag[i] = 0;
}
cin >> K;
for(int i=0; i<K; ++i){
cin >> data[i];
}
for(int i=0; i<K; ++i){
temp = data[i];
while (temp != 1){
if(temp % 2 == 0){
temp /= 2;
}else{
temp = (temp * 3 + 1) / 2;
}
if(in(temp, data, K)){
flag[temp] = -1;
}
}
}
vector<int> answer;
for(int i=0; i<K; ++i){
if(flag[data[i]] == 0){
answer.push_back(data[i]);
}
}
sort(answer.begin(), answer.end());
for(int i=answer.size()-1; i>0; --i){
cout << answer[i] << " ";
}
cout << answer[0] << endl;
return 0;
}
bool in(int temp, int* data, int K){
for(int i=0; i<K; ++i){
if(temp == data[i]){
return true;
}
}
return false;
}
- Java Version: 使用了“桶排序”的思想降序输出所有‘关键数’。
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int K = in.nextInt();
// 接收输入的正整数K
int[] inpus = new int[110];
// 定义一个定长的整型数组,数组长度>100,java会自动把数组初始化为0.
for(int i=0;i<K;++i) {
inpus[i] = in.nextInt();
}
// 接收输入的K个正整数
int[] flags = new int[110];
// 标记,flags[i] == -1 表示 inpus[i]不是一个"关键数";
// flags[i] == 0表示inpus[i]是一个关键数
int tmp = 0;
for(int i=0;i<K;++i) {
tmp = inpus[i];
while(tmp != 1) {
if(tmp % 2 == 0) {
tmp = tmp / 2;
}else {
tmp = (tmp * 3 + 1) / 2;
}
for(int j=0;j<K;++j) {
// 如果inpus[j]在tmp变1的过程中出现,就不是一个'关键数'
if(inpus[j] == tmp) {
flags[j] = -1;
}
}
}
}
// 处理完毕,所有flags[i] == 0 对应的inpus[i]就是所有的"关键数"
in.close();
// 关闭输入流
// 接下来就是将这些数字降序排序后输出。由于输入的K个不同的数字的大小都是
// (1, 100], 我们可以运用"桶排序"的思想用一个标记数组,按降序遍历输出。
int[] answer_flags = new int[110];
// 稍大一点的标记数组
int counter = 0;
for(int i=0;i<K;++i) {
if(flags[i] == 0) {
counter ++;
// 统计有多少个'关键字'
answer_flags[inpus[i]] = 1;
// 打上标记
}
}
for(int i=100; i>1; --i) {
// 降序输出
if(answer_flags[i] == 1) {
if(counter > 1) {
System.out.print(i + " ");
}else {
System.out.print(i);
}
counter --;
// 按格式输出,最后一个没有空格伴随。
}
}
}
}
易错点:
- 要搞清楚题目中关键数的定义
总结: