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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章