1104 天长地久 (20 分)

分析

  • 通过率感人的题目

  • 考点

    • 搜索
    • 剪枝
    • 排序
    • 质数
    • 最大公约数
  • 思路

    • 首先看到数值的位数最高达9位,所以暴力枚举肯定是会超时的,然后看到数据范围比较小,可以直接枚举加剪枝。
    • 我一直卡在第三个测试点,这里是需要对结果进行排序。后来我直接用set存,然后输出就对了。
  • 注意:

    • 这道题还可以考查一些东西,虽然我想到了,但是题目没有考,反而考了一个排序,没有仔细读题,害我找了半天。
      1. 如果位数最大包含10位,这里可以考查整数溢出。
      2. 正整数的首位不能为0,比如010,虽然占了三位,但是数值本质是两位,要小心。
  • 详细讲解

    • 见B站视频:我不是匠人。

AC代码

#include <iostream>
#include <cmath>
#include <unordered_set>
#include <set>
using namespace std;
bool tag;
unordered_set<int> st;
using LL = long long;
int gcd(int a, int b) {
	return b==0?a:gcd(b, a%b);
}
bool isPrime(int x){
	if(x <= 1) return false;
	int sq = sqrt(x);
	for(int i = 2; i <= sq; i++) {
		if(x % i == 0) return false;
	}
	return true;
}
set<pair<int,LL>> res;
void dfs(int u, int K, int m, int sum, LL A) {
	if(u > K) return;
	if(sum + (K-u) > m) return;
	if(sum + (K-u)*9 < m) return;
	if(u == K) {
		if(sum == m) {
			LL val = A + 1;
			int n = 0;
			do{
				n += val%10;
				val /= 10;
			}while(val);
			
			if(st.count(n)){
				res.insert({n, A});
				tag = true;
			} 
		} 
		return;	
	}
	for(int i = 0; i <= 9; i++) {
        if(!u && !i) continue;
		dfs(u+1, K, m, sum + i, A*10ll+i);
	}
}

int main() {
	int N;
	cin >> N;

	
	for(int i = 1; i <= N; i++) {
		printf("Case %d\n", i);
		tag = false;
		int K, m;
		cin>>K>>m;
		st.clear();
		for(int i = 1; i <= 90; i++) {
			auto t = gcd(i, m);
			if(t > 2 && isPrime(t)) {
				st.insert(i);
				
			}
		}
		res.clear();
		dfs(0, K, m, 0, 0);
		
		if(!tag) puts("No Solution"); 
		else{
			for(auto x: res) {
				printf("%d %lld\n", x.first, x.second);
			}
		}
	}
	
	
	return 0;
} 

视频版

/*
已知: A 和 m
 
1. A + 1的各位数之和 n
暴力解法
每一位上的数字
----
1 2 3 4 
K
0~9 
注意: 数字的首位不能为0

1. dfs去枚举每一个位上的数值
2. 剪枝  K = 9  6 3      9+27  < m = 60
3. tag标记有解的情况
4. 要按照顺序输出 n, A递增 
5. 求素数
6. 求最大公约数 
7. 10位,int会溢出 
*/
#include <iostream>
#include <cmath>
#include <unordered_set>
#include <set>
using namespace std;
bool tag;
unordered_set<int> st;
int gcd(int a, int b) {
	return b==0?a:gcd(b, a%b);
}
bool isPrime(int x){
	if(x <= 1) return false;
	int sq = sqrt(x);
	for(int i = 2; i <= sq; i++) {
		if(x % i == 0) return false;
	}
	return true;
}
set<pair<int, int>> res;
void dfs(int u, int K, int m, int sum, int A) {
	if(u > K) return;
	if(sum + (K-u)*9 < m) return;
	if(u == K){
		if(sum == m) {
			int B = A + 1;
			int n = 0;
			do{
				n += B%10;
				B /= 10;
			}while(B);
			if(st.count(n)) {
				tag = true;
				res.insert({n, A});
			}
		}
		return;
	}
	
	for(int i = 0; i <= 9; i++) {
		if(!u && !i) continue;
		dfs(u+1, K, m, sum+i, A*10+i);
	}
}
int main() {
	int N;
	cin >> N;
	for(int i = 1; i <= N; i++) {
		printf("Case %d\n", i);
		int K, m;
		cin >> K >> m;
		tag = false;
		//预处理m, n 
		st.clear();
		res.clear();
		for(int n = 1; n <= 90; n ++) {
			auto t = gcd(n, m);
			if(t > 2 && isPrime(t)){
				st.insert(n);
			}
		}
		// 深度搜索
		dfs(0, K, m, 0, 0);
		if(!tag) puts("No Solution");
		else{
			for(const auto&x: res) {
				printf("%d %d\n", x.first, x.second);
			}
		} 
	}
	return 0;
} 














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值