2019年9月 PAT甲级 7-1 Forever 题解
题意
输入N对K和m,要求找到所有K位数满足:
1.该数的和为m;
2.该数+1的和为n;
3.m和n的最大公约数是一个大于2的质数;
思路
K<10,最大为9,暴力枚举是10的9次,肯定超时,需要缩小枚举范围。
看到这题,我觉得一种数学直觉是大家都会有的,那就是:任意一个数p和p+1一般都是互质的吧。事实也是如此(我一时没想到反例)。
这就意味着,要是一个数简单+1,若没有进位,那么肯定无法符合要求,因为这样n和m也只相差一,它们是互质的(最大公约数为1,不是大于2的质数)。于是,我们就知道了,我们要枚举的是有进位的那些数。也就是尾数为9、99、999、9999……的那些数。而且,更有趣的是,我们可以根据尾数情况直接得到n和m的差值!举个例子9+1=10,位数和从9到1,m-n=8;99+1=100,位数和从18到100,m-n=17。(代码里的yes函数就在判断这个)
考虑到位数和的范围是其实很小,是1-90,也就是说m和n都是1-90;那我们就可以直接去算1-90这些数,哪些组合是符合条件3的?可以直接打表看。这就是我下面init函数里做的事儿。处理结果mn[i][j]为true表明(i,j)是符合要求的(m,n)对。事实上,根据m、n的差值和mn数组,可以进一步缩小可能的数位和,m和n的差因为是进位造成的,所以是有限制的,只能是17、26、35、44这些数(可以通过我注释掉的代码验证)。也就是说,我们只需要枚举以99、999、9999、99999为位数的数即可。
那剩下来的任务就是枚举了。为了控制好不同位数9尾数的进位,我们需要对前面剩余x个数位从10的x-1次方(形式为10…0)枚举到10的x次方减2(形式为99…98)。枚举代码在main函数里,草稿纸上写写对应一下下标就出来了。枚举并根据mn数组判断,最后把答案加到set里并输出。
至此,本场考试最难的一道题成功AC!(我没看AC率,做到后面才知道这道题过的人最少)代码如下:
#include <iostream>
#include <string>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <queue>
#include <set>
using namespace std;
bool mn[100][100];//[i][j]表示(i,j)是符合要求的数对。
int gcd(int a,int b){
//辗转相除法求最大公约数
if(b==0)