7-4 天长地久 (20分)

题目

“天长地久数”是指一个 K 位正整数 A,其满足条件为:A 的各位数字之和为 m,A+1 的各位数字之和为 n,且 m 与 n 的最大公约数是一个大于 2 的素数。本题就请你找出这些天长地久数。

输入格式:
输入在第一行给出正整数 N(≤5),随后 N 行,每行给出一对 K(3<K<10)和 m(1<m<90),其含义如题面所述。

输出格式:
对每一对输入的 K 和 m,首先在一行中输出 Case X,其中 X 是输出的编号(从 1 开始);然后一行输出对应的 n 和 A,数字间以空格分隔。如果解不唯一,则每组解占一行,按 n 的递增序输出;若仍不唯一,则按 A 的递增序输出。若解不存在,则在一行中输出 No Solution。

输入样例:
2
6 45
7 80
输出样例:
Case 1
10 189999
10 279999
10 369999
10 459999
10 549999
10 639999
10 729999
10 819999
10 909999
Case 2
No Solution

解题思路

1.辗转相除法求最大公约数

必须掌握如何求最大公约数(greatest common divisor):辗转相除法

int gcd(int m, int n){
	if (m < n){
		int t = m;
		m = n;
		n = t;
	}
	return n == 0 ? m : gcd(n, m%n);
}

辗转相除法是求最大公约数的一种方法。它的具体做法是:用较小数除较大数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。
如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。这个和更相减损术有着异曲同工之处。

原理:

首先介绍下更相减损术的原理,假设有两个数161和63,我们要求这两个数的最大公因数,不妨假定这个最大公因数为m,我们可以将较大的数161看成63+98,63与98的和161可以被m整除,其中63也可以被m整除,自然98可以被m整除;
所以这个问题就转换为求98和63的最大公因数m(和上面m相等)
将98看成63+35,其中63可以被m整除,和98也能被m整除,故35也可以被m整除;
所以问题进一步转换为求35和63的最大公因数m(和上面m相等)
同理转换为求 (63-35)=>28和35 的最大公因数
然后转换为求28和7的最大公因数
…(一直减呀减)
后来转换为求7和7的最大公因数
最后转换为求7和0的最大公因数
输出第一个数字即可;这就是相减损术的原理
我们发现求28和7的最大公约数,一直减7,一直减7…减到不能减为止。这个不断减7的过程就是除7求余数(即%7)
这样我们可以将相减损术优化成辗转相除法

同理计算最小公倍数:

只要用这两个数的乘积除以最大公因数即可
printf(“lcm=%d\n”,a*b/gcd(a,b))
————————————————
原文链接:https://blog.csdn.net/weixin_43886797/article/details/85569998

2.分情况讨论在哪些数之间暴搜

2.忽略如何取数的过程,分情况讨论(A)和(A+1)的各数和m,n的最大公约数应为多少

涉及到了进位与否,分为以下几种:

  1. 当A的末尾数不为9时,A+1不进位

    n=m+1 互质,不满足条件

  2. 当A的末尾数为9时,A+1进一位

    例:129和130(12和4);159和160(15和7)
    n=m+8,则最大公约数必然是8的因子(1,2,4,8),唯一的质数2不满足条件

  3. 当A的末尾数为99时,A+1进两位
    例:199和200(19和2);1099和1100(19和2)
    n=m+17,则 最大公约数必然是17的因子(1,17),17就满足条件

从末尾数为99开始就出现了满足条件的数,则最起码末尾两位为99不变进行暴搜寻找符合条件的数,大大减少了暴搜的次数
在这里插入图片描述

使用暴搜的原因是因为和dfs一样都得搜索这么多遍

bool cmp(yys a, yys b){
	if (a.n != b.n)
		return a.n < b.n;
	else
		return a.num < b.num;
}
void tcdj(int k, int m){
	long long int minm=1, i,t;//minm即为k-2位的最小数(最后两位是99),i是暴搜的数
	int n,sum=0;
	yys yy[10001];//有缘数的集合
	for (int j = 1; j < (k-2); j++)
		minm *= 10;
	//直接从10````99这个最小数开始循环,每次加100,这样尾数就不会变
	for (i = minm * 100 + 99; i < minm * 1000; i += 100){
		if (compute(i) == m){
			n = compute(i + 1);
			if (issu(gcd(m, n))){
				yy[sum].num = i;
				yy[sum++].n = n;
			}
		}
	}
	if (sum==0)
		printf("No Solution\n");
	else{
		sort(yy,yy+sum, cmp);
		for (int j = 0; j < sum; j++)
			printf("%d %lld\n", yy[j].n, yy[j].num);
	}
}

AC代码

#include<stdio.h>
#include<string>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int N, num[10][2];
typedef struct yys{
	long long int num;
	int n;
}yys;
int gcd(int m, int n){
	if (m < n){
		int t = m;
		m = n;
		n = t;
	}
	return n == 0 ? m : gcd(n, m%n);
}
bool issu(int x){
	if (x < 3)return false;
	else
		for (int i = 2; i <= sqrt(x); i++)
			if (x%i == 0)return false;
	return true;
	
}
int compute(long long int t){
	int len = 0;
	while (t != 0){
		len += t % 10;
		t /= 10;
	}
	return len;
}
bool cmp(yys a, yys b){
	if (a.n != b.n)
		return a.n < b.n;
	else
		return a.num < b.num;
}
void tcdj(int k, int m){
	long long int minm=1, i,t;//minm即为k-2位的最小数(最后两位是99),i是暴搜的数
	int n,sum=0;
	yys yy[10001];//有缘数的集合
	for (int j = 1; j < (k-2); j++)
		minm *= 10;
	//直接从10````99这个最小数开始循环,每次加100,这样尾数就不会变
	for (i = minm * 100 + 99; i < minm * 1000; i += 100){
		if (compute(i) == m){
			n = compute(i + 1);
			if (issu(gcd(m, n))){
				yy[sum].num = i;
				yy[sum++].n = n;
			}
		}
	}
	if (sum==0)
		printf("No Solution\n");
	else{
		sort(yy,yy+sum, cmp);
		for (int j = 0; j < sum; j++)
			printf("%d %lld\n", yy[j].n, yy[j].num);
	}
}
int main(){
	scanf("%d", &N);
	for (int i = 1; i <= N; i++){
		scanf("%d%d", &num[i][0], &num[i][1]);
	}
	for (int i = 1; i <= N; i++){
		printf("Case %d\n", i);
		tcdj(num[i][0], num[i][1]);
	}
	system("PAUSE");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章