PAT(甲级)2019年秋季考试 7-1 Forever (20 分) 凌宸1642

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

题目描述:

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

  • 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.

译:“永恒数” 是一个有 K 位数的整数 A ,满足以下条件:

  • 数字 A 的所有数字之和为 m ;
  • 数字 A + 1 的所有数字之和为 n ;
  • m 和 n 的最大公因数是一个大于 2 的素数。

现在你应该找出这些永恒数。


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.

译:每个输入文件包含一个测试用例,对于每个测试用例,第一行包含一个整数 N (≤5) 。 接下来 N 行,每行给定一对数字 K (3<K<10) 和 m (1<m<90) , 其中的含义在问题描述中给出。


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.

译:对于每一对 K 和 m ,第一行输出 Case X, X 是 1开始的用例索引。接下来打印 n 和 A。数字之间必须用一个空格隔开。如果解不唯一,按照 n 的升序输出,如果解依旧不唯一,则按照 A 的升序输出。如果没有解,则输出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

The Idea 1:

首先这一题我是按照大模拟思路来做的,所以我们需要完成以下几个函数的实现:

  • 求数字 A 的所有数字之和的 函数 getSumOfDigits(int a)
  • 找出所有数字和为 m 的 最小的 k 位整数 findMin(int k , int m)
  • 找出所有数字和为 m 的 最大的 k 位整数 findMax(int k , int m))
  • 求两个整数的最大公因数的函数 gcd(int a , int b)
  • 判断一个数是否为素数的函数 isPrime(int x)
  • 然后从最小值开始,循环到最大值,将满足题目要求的解记录在 数组vector<pair<int ,int>结构体中,然后按照题目要求排序输出,如果找不到解,则输出提示信息 No Solution

注意:上述循环过程中步长的选择,刚开始选择步长为 1 ,然后竟然是没有超时的;后面仔细想想,其实步长可以设置为 9 ,因为确定好数后,所有满足要求的数一定会满足,一个数加 1 另一个数 减 1(例如 123456 数位和为 21 ,下一个就是 123465 , 十位上的 5 加 1 ,个位上的 6 减 1,所以可以设置步长为 9 )。


The Codes 1:

#include<bits/stdc++.h>
using namespace std ;
int N , k , m , n ; 
typedef pair<int ,int> PII ;
int sumOfDigits(int x){ // 求数字 A 的所有数字之和
	int res = 0 ;
	while(x){
		res += x % 10 ;
		x /= 10 ;
	}
	return res ;
}
int gcd(int a , int b){ // 求两个整数的最大公因数
	return !b? a : gcd(b , a % b) ; 
}
bool isPrime(int x){ // 判断一个数是否为素数
	if(x == 1 || x == 2)return true ;
	for(int i = 2 ; i * i <= x ; i ++)
		if(x % i == 0) return false ;
	return true ;
}
int findMin(int k , int m){ // 找出所有数字和为 m 的 最小的 k 位整数
	int res = 0 , cnt = 0 ;
	while(m > 9) {    // 从个位开始,当数位和大于  9 时,每位都取 9
		res = res * 10 + 9 ; 
		m -= 9 ; 
		cnt ++ ;
	}
	if(m > 1) res = (m - 1) * pow(10 , cnt) + res ; // 留 1 给最高位,剩余数字放第一个 9 之前
	res = pow (10 , k - 1) + res ; // 最高位为 1 ,加上其他的数字组成一个 k 位数
	return res ;
}
int findMax(int k , int m){ // 找出所有数字和为 m 的 最大的 k 位整数
	int res = 0 , cnt = 0 ;
	while(m > 9) { // 从第 k 位开始,当数位和大于  9 时,每位都取 9
		res = res * 10 + 9 ; 
		m -= 9 ; 
		cnt ++ ;
	}
	res = res * 10 + m ; // 剩下的数字给 最后一个 9 之的位置
	k -= (cnt + 1) ; // 看此时还有几位需要填充
	while(k --) res *= 10 ;
	return res ;
}
bool cmp(PII a , PII b){
	if(a.first != b.first) return a.first < b.first ;
	return a.second < b.second ;
}
int main(){
	scanf("%d" , &N) ;
	for(int i = 1 ; i <= N ; i ++){
		scanf("%d%d" , &k , &m) ;
		cout << "Case " << i << endl ;
		if(m > k * 9) { // 每一位都取 9 得到的数位和,比 m 小,则一定无解
			cout << "No Solution" << endl ;
			continue ;
		}
		vector<PII> ans ;
		int maxn = findMax(k , m) , x ;           // 步长为 9 即 j += 9 
		for(int j = findMin(k , m) ; j <= maxn ; j ++){ // 步长为 1 ,竟然不会超时,暴力有救
			if(sumOfDigits(j) == m){
				x = sumOfDigits(j + 1) ;
				int g = gcd(m , x) ;
				if(g > 2  && isPrime(g) ) ans.push_back({x , j}) ;  
			}
		} 
		if(ans.size() == 0){ // 测试点 2  
			cout << "No Solution" << endl ;
			continue ;
		}
		sort(ans.begin() , ans.end() , cmp) ;
		for(int j = 0 ; j < ans.size() ; j ++) 
			printf("%d %d\n" , ans[j].first , ans[j].second) ; 
	}
	return 0 ;
}


The result 1:

步长为 1
在这里插入图片描述

步长为 9

在这里插入图片描述


The Idea 2:

  • 这一题看网上很多大佬都会选择使用 DFS + 剪枝 来做,然后自己也尝试了一下

The Codes 2:

#include<bits/stdc++.h>
using namespace std ;
#define x first 
#define y second
typedef pair<int , int> PII ;
vector<PII> ans ;
int N , k , m ;
// 求数字 A 的所有数字之和
int getSumOfDigits(int a){ 
	int res = 0 ;
	while(a){
		res += a % 10 ;
		a /= 10 ;
	}
	return res ;
}
// 求两个整数的最大公因数
int gcd(int a , int b){  
	if(b == 0) return a ;
	return gcd(b , a % b) ;
}
// 判断一个数是否为素数
bool isPrime(int a){
	if(a == 1 || a == 2) return true ;
	for(int i = 2 ; i * i <= a ; i ++)
		if(a % i == 0) return false ;
	return true ;
}

bool cmp(PII a , PII b){
	if(a.x != b.x) return a.x < b.x ;
	return a.y < b.y ;
}
// nowK 表示现在是 几位数 , nowSum 是现在这几位数的所有数字之和 , nowNum ,是具体组成的数字
void dfs(int nowK , int nowSum , int nowNum){
	if(nowSum == m && nowK == k){
		int n = getSumOfDigits(nowNum + 1) ;
		int g = gcd(m , n) ;
		if(g > 2 && isPrime(g)) ans.push_back({n , nowNum}) ;
		return ;
	}
	if(nowSum > m || nowK > k || nowSum + (k - nowK) * 9 < m) return ; // 剪枝
	for(int i = 0 ; i < 10 ; i ++)
		dfs(nowK + 1 , nowSum + i , nowNum * 10 + i) ; 
}
int main(){
	scanf("%d" , &N) ;
	for(int i = 1 ; i <= N ; i ++){
		scanf("%d%d" , &k , &m)  ;
		printf("Case %d\n" , i) ;
		if(m > k * 9){
			printf("No Solution\n") ;
			continue ;
		}
		ans.clear() ;
		dfs(0 , 0 , 0) ;
		if(ans.size() == 0) printf("No Solution\n") ; // 测试点 2
		else {
			sort(ans.begin() , ans.end() , cmp) ;
			for(int j = 0 ; j < ans.size() ; j ++)
				printf("%d %d\n" , ans[j].x , ans[j].y) ;
		}
	}
	return 0 ;
} 

The result 2:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lingchen0522

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值