「PAT乙级真题解析」Basic Level 1104 天长地久 (问题分析+完整步骤+伪代码描述+提交通过代码)

题设要求给定一个位数K, 一个各位数字之和m, 要求满足条件的A. 需要满足的条件是: A+1的各位数字之和n与给定的m的最大公约数是一个大于2的素数。 题目就是有点绕, 其他还好。 最容易想到的方式就是穷举进行条件检测, 但是会有性能问题, 比如测试点5对穷举的作答就会提示运行超时。

#算法#数据结构

Table of Contents

乙级的题目训练主要用来熟悉编程语言的语法和形成良好的编码习惯和编码规范。从小白开始逐步掌握用编程解决问题。

PAT (Basic Level) Practice 1104 天长地久

问题分析

题设要求给定一个位数K, 一个各位数字之和m, 要求满足条件的A. 需要满足的条件是: A+1的各位数字之和n与给定的m的最大公约数是一个大于2的素数。 题目就是有点绕, 其他还好。 最容易想到的方式就是穷举进行条件检测, 但是会有性能问题, 比如测试点5对穷举的作答就会提示运行超时。

性能优化

  1. 缓存结果,避免重复计算。
  2. 只检查末尾是连续的9的数值。
    • 一个数值如果末尾不是连续的9, 那么就不会是满足题设条件的数。
    • 原因在于:
      • 如果末尾不是连续的9, 则A+1不会发生进位
      • 意味着A+1的各位数字之和n=m+1, (m+1, m)的最大公约数必然是1, 不满足题设条件

生成末尾是连续的9的数值

  1. 假设末尾有连续x个9, x取值区间为[1, m], 则:
    • 前面(m-x)位部分组成的数值取值区间为[10^(m-x-1), 10^(m-x))
    • 对于每一个x, 需要检查的数值个数为: 90^(m-x-1)
    • 需要跳过前面的数值也末尾是9的情况, 防止输出重复答案
  2. 总共需要检查的数值个数就是:
    • Σ(90^(m-x-1)), x∈[1, m]

完整描述步骤

  1. 获取输入: 位数K, 各位数字之和m
  2. 初始化记录器:
    • solutions = {}
  3. 对于9的位数x从1到m:
    • 对于需要检查的数前一部分, 最大数值和最小数值为: 10^(m-x)-1, 10^(m-x)
    • 对于每一个数number:
      • 如果各位数字之和等于m:
        • 计算number+1的各位数字之和n
        • 如果(m, n)的最大公因数大于2 并且 是素数:
          • 将(n, number)放入solutions中
  4. 如果solutions中没有元素, 则输出"No Solution"
  5. 如果solutions中有数据, 按照n的递增序排列, n相同的按照number的递增序排列, 然后依次输出答案;

伪代码描述

  1. get input: case_amount
  2. for each case:
    • get input: digits_amount, digits_sum
    • init recorder:
      • solutions = {}
    • for amount_of_nine in range(1, digits_amount + 1):
      • max_number = 10 ^ (digits_amount - amount_of_nine)
      • min_number = max_number / 10
      • for prefix_number in range(min_number, max_number):
        • if prefix_number % 10 == 9:
          • continue
        • number_to_check = int("{prefix_number}{'9' * amount_of_nine}")
        • if calculate_digits_sum(number_to_check) == digits_sum:
          • digits_sum_of_plus_1 = calculate_digits_sum(number+1)
          • gcd_two_digits_sum = gcd(m, digits_sum_of_plus_1)
          • if gcd_two_digits_sum > 2 and is_prime_number(gcd_two_digits_sum):
            • solutions.append((digits_sum_of_plus_1, number_to_check))
    • if len(solutions) == 0:
      • print("No Solution")
    • else:
      • sorted(solutions)
        • ascending by digits_sum_of_plus_1
        • and by number_to_check secondly
      • print(solutions)

完整提交代码

/*
# 问题分析
题设要求给定一个位数K, 一个各位数字之和m, 要求满足条件的A.
需要满足的条件是: A+1的各位数字之和n与给定的m的最大公约数是一个大于2的素数。
题目就是有点绕, 其他还好。
最容易想到的方式就是穷举进行条件检测, 但是会有性能问题, 比如测试点5对穷举的作答就会提示运行超时。
 
## 性能优化
1. 缓存结果,避免重复计算。
2. 只检查末尾是连续的9的数值。
    - 一个数值如果末尾不是连续的9, 那么就不会是满足题设条件的数。
    - 原因在于: 
        - 如果末尾不是连续的9, 则A+1不会发生进位
        - 意味着A+1的各位数字之和n=m+1, (m+1, m)的最大公约数必然是1, 不满足题设条件
 
## 生成末尾是连续的9的数值
1. 假设末尾有连续x个9, x取值区间为[1, m], 则:
    - 前面(m-x)位部分组成的数值取值区间为[10^(m-x-1), 10^(m-x))
    - 对于每一个x, 需要检查的数值个数为: 90^(m-x-1)
    - 需要跳过前面的数值也末尾是9的情况, 防止输出重复答案
2. 总共需要检查的数值个数就是:
    - Σ(90^(m-x-1)), x∈[1, m]
 
## 完整描述步骤
1. 获取输入: 位数K, 各位数字之和m
2. 初始化记录器:
    - solutions = {}
3. 对于9的位数x从1到m:
    - 对于需要检查的数前一部分, 最大数值和最小数值为: 10^(m-x)-1, 10^(m-x)
    - 对于每一个数number:
        - 如果各位数字之和等于m:
            - 计算number+1的各位数字之和n
            - 如果(m, n)的最大公因数大于2 并且 是素数:
                - 将(n, number)放入solutions中
4. 如果solutions中没有元素, 则输出"No Solution"
5. 如果solutions中有数据, 按照n的递增序排列, n相同的按照number的递增序排列, 然后依次输出答案;
 
## 伪代码描述
1. get input: case_amount
2. for each case:
    - get input: digits_amount, digits_sum
    - init recorder:
        - solutions = {}
    - for amount_of_nine in range(1, digits_amount + 1):
        - max_number = 10 ^ (digits_amount - amount_of_nine)
        - min_number = max_number / 10
        - for prefix_number in range(min_number, max_number):
            - if prefix_number % 10 == 9:
                - continue
            - number_to_check = int("{prefix_number}{'9' * amount_of_nine}")
            - if calculate_digits_sum(number_to_check) == digits_sum:
                - digits_sum_of_plus_1 = calculate_digits_sum(number+1)
                - gcd_two_digits_sum = gcd(m, digits_sum_of_plus_1)
                - if gcd_two_digits_sum > 2 and is_prime_number(gcd_two_digits_sum):
                    - solutions.append((digits_sum_of_plus_1, number_to_check))
    - if len(solutions) == 0:
        - print("No Solution")
    - else:
        - sorted(solutions)
            - ascending by digits_sum_of_plus_1
            - and by number_to_check secondly
        - print(solutions)
*/
 
#include<stdio.h>
#include<stdlib.h>
# include<math.h>
 
int cached_is_prime_result[90] = {-1};
int cached_gcd[90][90] = {0};
typedef struct solution{
    int number;
    int digits_sum_of_plus_1;
} solution;
 
 
int is_prime_number(int number){
    if (number == 2){
        return 1;
    }
    
    if (cached_is_prime_result[number] != -1){
        return cached_is_prime_result[number];
    }
    
    int digit = number % 10;
    
    if (digit != 1 && digit != 3 && digit != 5 && digit != 7 && digit != 9 ){
        cached_is_prime_result[number] = 0;
        return cached_is_prime_result[number];
    }
    
    for(int factor = 2; factor <= sqrt(number); factor++){
        if(number % factor == 0){
            cached_is_prime_result[number] = 0;
            return cached_is_prime_result[number];
        }
    }
    
    cached_is_prime_result[number] = 1;
    return cached_is_prime_result[number];
}
 
int gcd(int a, int b){
    if (cached_gcd[a][b]){
        return cached_gcd[a][b];
    }
    
    if (a % b == 0){
        cached_gcd[a][b] = b;
    } else {
        cached_gcd[a][b] = gcd(b, a % b);
    }
    
    return cached_gcd[a][b];
}
 
int calculate_digits_sum(int number){
    int digits_sum = 0;
    int remainder;
    while (number != 0){
        remainder = number % 10;
        digits_sum += remainder;
        number /= 10;
    }
 
    return digits_sum;
}
 
 
int cmp(const void* a, const void* b){
    solution solution_A = *(solution*)a;
    solution solution_B = *(solution*)b;
 
    if (solution_A.digits_sum_of_plus_1 != solution_B.digits_sum_of_plus_1){
        return solution_A.digits_sum_of_plus_1 > solution_B.digits_sum_of_plus_1;
    }
 
    return solution_A.number > solution_B.number;
}
 
int generate_number(int prefix_number, int amount_of_nine) {
    int result = prefix_number;
    for (int i = 0; i < amount_of_nine; i++) {
        result = result * 10 + 9;
    }
    return result;
}
 
int is_meet_conditions(int digits_sum, int number_to_check) {
    if (calculate_digits_sum(number_to_check) != digits_sum) {
        return 0;
    }
 
    int digits_sum_of_plus_1 = calculate_digits_sum(number_to_check + 1);
    int gcd_of_two_digits_sum = gcd(digits_sum, digits_sum_of_plus_1);
    if (gcd_of_two_digits_sum > 2 && is_prime_number(gcd_of_two_digits_sum)) {
        return 1;
    }
 
    return 0;
}
 
 
solution solutions[10000000000];
 
int main(){
    for (int i = 0; i < 90; i++){
        cached_is_prime_result[i] = -1;
    }
 
    int case_amount;
    scanf("%d", &case_amount);
    
    int digits_amount, digits_sum;
    int min_number, max_number, number_to_check;
    int solution_amount;
    for (int i = 0; i < case_amount; i++){
        solution_amount = 0;
        scanf("%d %d", &digits_amount, &digits_sum);
 
        for (int amount_of_nine = 1; amount_of_nine <= digits_sum; amount_of_nine++){
            max_number = 1;
            for (int j = 0; j < digits_amount - amount_of_nine; j++){
                max_number *= 10;
            }
            min_number = max_number / 10;
            for (int j = min_number; j < max_number; j++){
                if (j % 10 == 9){
                    continue;
                }
                
                number_to_check = generate_number(j, amount_of_nine);
                if (is_meet_conditions(digits_sum, number_to_check)){
                    solutions[solution_amount].number = number_to_check;
                    solutions[solution_amount].digits_sum_of_plus_1 = calculate_digits_sum(number_to_check+1);
                    solution_amount++;
                }
            }
        }
 
        printf("Case %d\n", i+1);
        if (solution_amount == 0){
            printf("No Solution\n");
        } else {
            qsort(solutions, solution_amount, sizeof(solution), cmp);
            for (int j = 0; j < solution_amount; j++){
                printf("%d %d\n", solutions[j].digits_sum_of_plus_1, solutions[j].number);
            }
        }
    }
    
    
    
    return 0;
}
「PAT乙级真题解析」Basic Level 1104 天长地久 (问题分析+完整步骤+伪代码描述+提交通过代码) | 生活糖果