1160. Forever (20)-PAT甲级真题 (数学 DFS变形)

题意

永恒数的定义:对于一个k位数字A,其各位和为m,若A+1的各位和为n,m和n的最大公约数为大于2的质数,则A为永恒数。
询问n次,每次给出k与m,询问所有满足要求的n与A,并按序输出。若无则输出no solution

思路

  • 首先思考遍历所有k位数的方法的最大时间复杂度O(n * pow(k, 10) * k) = 5E10,太大了。
  • 所以必须找数学规律简化:
    • 发现最后一个必须是9,不然数字加和之差仅为1
    • 然后发现倒数第二个也得是9,不然数字加和之差为8 - 最大公约数一定为2的倍数。
    • 这么就不用遍历pow(k, 10),最大pow(7, 10)次,合计5E8,由于给了3s的限制,测试了下刚好过(n == 20 时TLE)。对应于解法1.
  • 简化的思路1:每次j++后都要对j的所有数位和重新计算,而大部分数位和都是+1.思考可以DFS的方法,将数字A抽象为k层,每次传参cnt与level记录当先在第几位与前level位之和。然后剪枝。
  • 简化的思路2:倒数第二个为9的差为17,倒数第三个为9的差为26,依次得到35、44、53、62、71,对应于质数为17、13、5、7、11、53、31、71。也就是m为上述倍数才可能为备选,但难以通过这些质数的倍数作为m反向构造a,应该走不通。

总结

  • 注意确定思路后先计算时间复杂度。
  • 常规最优解的时间复杂度也超时时,思考通过寻找数学规律以寻求降低时间复杂度。
  • 注意字符串、数字转换为DFS的思路。

解法1 - 数学规律

#include<bits/stdc++.h>
using namespace std;
bool isbpri(int x){
	if(x <= 2) return false;
	for(int i = 2; i <= x / i; i ++)
		if(x % i == 0) return false;
	return true; 
}
int main(){
	int n, k, m; cin>>n;
	for(int i = 1; i <= n; i ++){
		printf("Case %d\n", i);
		cin>>k>>m;// k > 3
		int low = pow(10, k - 3), high = pow(10, k - 2);
		set<pair<int, int>> ans;
		for(int j = low; j < high; j ++){
			int tmp = j, tar = 18, tarn = 0;
			while(tmp){
				tar += tmp % 10;
				tmp /= 10;
			}
			if(tar != m) continue;
			tmp = j + 1;
			while(tmp){
				tarn += tmp % 10;
				tmp /= 10;
			}
			if(isbpri(__gcd(tar, tarn)))
				ans.insert({tarn, j * 100 + 99});
		}
		if(!ans.size()) puts("No Solution");
		else{
			for(auto i : ans) printf("%d %d\n", i.first, i.second);
		}
	}
	return 0;
}

解法2 - DFS

#include<bits/stdc++.h>
using namespace std;
int k, m, t;
bool isbpri(int x){
	if(x <= 2) return false;
	for(int i = 2; i <= x / i; i ++)
		if(x % i == 0) return false;
	return true; 
}
void dfs(int cnt, int sum, int x, set<pair<int, int>> & ans){
    if(cnt > k || sum > m || m > sum + 9*(k-cnt)) return;
    if(cnt == k && sum == m){
        int y = x + 1,n = 0;
        while(y){
			n += y%10;
			y /= 10;
		} 
        if(isbpri(__gcd(n,m))) ans.insert({n, x});
        return;        
    }
    for(int i = 0; i <= 9; i ++){
		if(sum == 0 && i == 0)continue;
		else if(cnt >= k-2){
			dfs(cnt+1,sum+9,x*10+9, ans);
			break;
		}else dfs(cnt + 1,sum + i,x*10 + i, ans);
	}
}
int main(){
    int t;scanf("%d", &t);
    for(int i = 1 ; i <= t ; i ++ ){
        printf("Case %d\n", i);
        scanf("%d %d", &k, &m);
        dfs(0,0,0, ans);
        if(!ans.size()) printf("No Solution\n");
		for(auto j : ans) printf("%d %d\n", j.first, j.second);
    }
    return 0;
}

题目

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

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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值