PAT(甲级)2019年秋季考试 7-1 Forever

7-1 Forever (20分)

"Forever number" is a positive integer A with K digits, satisfying the following constrains:git

  • the sum of all the digits of A is m;
  • the sum of all the digits of A+1 is n; and
  • the greatest common divisor of m and n is a prime number which is greater than 2.

Now you are supposed to find these forever numbers.算法

Input Specification:

Each input file contains one test case. For each test case, the first line contains a positive integer N (≤5). Then N lines follow, each gives a pair of K (3<K<10) and m (1<m<90), of which the meanings are given in the problem description.编程

Output Specification:

For each pair of K and m, first print in a line Case X, where X is the case index (starts from 1). Then print n and A in the following line. The numbers must be separated by a space. If the solution is not unique, output in the ascending order of n. If still not unique, output in the ascending order of A. If there is no solution, output No Solution.函数

Sample Input:

2
6 45
7 80

Sample Output:

Case 1
10 189999
10 279999
10 369999
10 459999
10 549999
10 639999
10 729999
10 819999
10 909999
Case 2
No Solution

题目限制:

image.png

题目大意:

现给定一个数字A的总位数K和全部位数之和m,要求你寻找这样的一个数字n,n为A+1的全部数位之和,m和n的最大公约数G,并G大于2且为素数,输出全部的n和A,不然输出No Solution。测试

算法思路:

第一反应此题和全排列的问题即为类似,就是添加了约束条件。那么其本质就是使用暴力搜索,很容易就想到使用DFS进行求解。可是直接进行DFS最后一个点会超时,因此得弄清楚剪枝条件,咱们假设如今搜索到第depth位,当前的总数位之和为m,数字为num,那么后面尚未搜索的数位有K-depth位。那么剪枝的条件就是,若是当前的总数位之和m+后面未搜索的最大总数位之和9*(K-depth)小于K,那么就说明不管怎样取值,都不可能存在一个数的总数位m符合题目要求。这样DFS的超时问题就解决了,如今只须要求解任意2数的最大公约数和任意一个数字的位数之和,就能够编写DFS函数了。优化

求解最大公约数的方法,这里给出2种:
// 适用于a,b较小的状况
int gcd(int a,int b){
    if(b==0) return a;
    return gcd(b,a%b);
}
// 适用于a,b较大的状况(编程之美2.7章节)
int gcd(int a,int b){
    if(a<b) return gcd(b,a);
    if(b==0){
        return a;
    } else {
        if(a%2==0){
            if(b%2==0){
                return 2*gcd(a/2,b/2);
            } else {
                return gcd(a/2,b);
            }
        } else{
            if(b%2==0){
                return gcd(a,b/2);
            } else {
                return gcd(b,a-b);
            }
        }
    }
}
对于任意一个数字num,其总位数之和:
int getDigitSum(int num){
    int m = 0;
    while (num!=0){
        m += num%10;
        num /= 10;
    }
    return m;
}
DFS函数:
/*
 * depth:当前深度,表明选择第几位,初始为0
 * m:当前已经选择的位数之和,好比选择了3次,分别是1,4,5,那么m=1+4+5=10
 * num:当前已经选择的位数所表明的数字,好比选择了3次,分别是1,4,5,那么num=145
 *
 */
void DFS(int depth,int m,int num){
    if(depth==K&&m==M){
        int n = getDigitSum(num+1);// 获得n
        int G = gcd(m,n);
        if(G>2&&isPrime(G)){
            // 保存结果集
            result.emplace_back(num,n);
        }
        return;
    }
    // 剩余最大值之和都小于所须要的,不可能有解
    if(m+9*(K-depth)<M||depth>K) return;
    for(int i=0;i<=9;++i){
        DFS(depth+1,m+i,num*10+i);
    }
}

到这里DFS并无结束,由于还有能够优化的点(上面的代码已经能够经过测试了),由于第一位必定是1,不多是0,最后两位必定是99。第一位是1比较好想的到,最后两位为何是99呢?spa

首先对于任意2个相邻数字的最大公约数为1,好比6和7,要想m和n的最大公约数大于2,首先就是要求m和n不能是相邻的两个数字,那么要求m和n不相邻,就得保证A的最后一个数字得是9,否则A+1没有进位,n=m+1。可是若是只有最后一位为9依然仍是不行,由于若是只有一个9,那么A+1的全部数位之和n=m-9+1(最后的9进位了),那么n与m的最大公约数等价于8和m的最大公约数(展转相除法,不懂得看编程之美2.7章节),可是8的约数只有1,2,4,8,均不是大于2的素数,因此最后一位还得是9,由于不是9的化,n依然等于m-8。最后2位为9之后,n=m-9-9+1(2个9都进位了),这样n和m的公约数就转化为m和17的公约数了,若是m刚好整除17,就获得了一个大于2的素数,因此最后2位必定是9。3d

根据以上分析,优化后的DFS以下:
/*
 * depth:当前深度,表明选择第几位,初始为0
 * m:当前已经选择的位数之和,好比选择了3次,分别是1,4,5,那么m=1+4+5=10
 * num:当前已经选择的位数所表明的数字,好比选择了3次,分别是1,4,5,那么num=145
 *
 */
void DFS(int depth,int m,int num){
    if(depth==K&&m==M){
        int n = getDigitSum(num+1);// 获得n
        int G = gcd(m,n);
        if(G>2&&isPrime(G)){
            result.emplace_back(num,n);
        }
        return;
    }
    // 剩余最大值之和都小于所须要的,不可能有解
    if(m+9*(K-depth)<M||depth>K) return;
    // 第一位必定为1,最后2位必定为9
    int i = depth==0 ? 1: depth==K-1||depth==K-2? 9 : 0;
    for(;i<=9;++i){
        DFS(depth+1,m+i,num*10+i);
    }
}

最后使用result保存全部的结果集,而后排序输出便可。code

注意点:

  • 一、排序是必须的,否则测试点2没法经过(卡了我1小时,本来觉得已经有序)

提交结果:

image.png

AC代码:

#include<cstdio>
#include<vector>
#include<cmath>
#include <algorithm>

using namespace std;

struct Node{
    int A;
    int n;
    Node(int _A,int _n){
        A = _A;
        n = _n;
    }
};

int K,M;// 总位数,总位数之和
vector<Node > result;

bool cmp(const Node &a,const Node &b){
    return a.n!=b.n?a.n<b.n:a.A<b.A;
}

int gcd(int a,int b){
    if(b==0) return a;
    return gcd(b,a%b);
}

bool isPrime(int n){
    if(n<=1) return false;
    int sqrtn = (int)sqrt(n*1.0);
    for(int i=2;i<=sqrtn;++i){
        if(n%i==0) return false;
    }
    return true;
}


int getDigitSum(int num){
    int m = 0;
    while (num!=0){
        m += num%10;
        num /= 10;
    }
    return m;
}
/*
 * depth:当前深度,表明选择第几位,初始为0
 * m:当前已经选择的位数之和,好比选择了3次,分别是1,4,5,那么m=1+4+5=10
 * num:当前已经选择的位数所表明的数字,好比选择了3次,分别是1,4,5,那么num=145
 *
 */
void DFS(int depth,int m,int num){
    if(depth==K&&m==M){
        int n = getDigitSum(num+1);// 获得n
        int G = gcd(m,n);
        if(G>2&&isPrime(G)){
            result.emplace_back(num,n);
        }
        return;
    }
    // 剩余最大值之和都小于所须要的,不可能有解
    if(m+9*(K-depth)<M||depth>K) return;
    // 第一位必定为1,最后2位必定为9
    int i = depth==0 ? 1: depth==K-1||depth==K-2? 9 : 0;
    for(;i<=9;++i){
        DFS(depth+1,m+i,num*10+i);
    }
}

int main(){
    int N;
    scanf("%d",&N);
    for(int i=0;i<N;++i){
        scanf("%d %d",&K,&M);
        printf("Case %d\n",i+1);
        result.clear();
        DFS(0,0,0);
        sort(result.begin(), result.end(),cmp);
        for (auto &item : result) {
            printf("%d %d\n",item.n,item.A);
        }
        if (result.empty()) {
            printf("No Solution\n");
        }
    }
    return 0;
}
相关文章
相关标签/搜索