PAT Class B "1104 Everlasting" DFS optimization ideas

 This article introduces the author's optimization ideas for B1104 . The AC code is in Case3 at the end of the article, and Case1-3 is the optimization idea.

Table of contents

Case 1:

Case 2:

Case 3:

If you do not have an answer, the author strongly recommends that you read Case 1-3 as needed, write the code yourself after reading it, think about optimization ideas, and implement it yourself. Share your optimization ideas in the comment area.


"Eternal number" refers to Ka positive integer with digits A, which satisfies the following conditions: Athe sum of the digits of is m, A+1the sum of the digits of is n, and the greatest common divisor of mand nis a prime number 2greater . This question asks you to find out these everlasting numbers.

Input format:

The input gives a positive integer in the first line N(\leq 5), followed Nby a pair of K(5 < K < 10)sums in each line m(1<m<90), and its meaning is as stated in the title.

Output format:

For each pair of input Kand m, first output in one line Case X, where Xis the output number (starting from 1); then output the corresponding nand in one line A, the numbers are separated by spaces. If the solution is not unique, each set of solutions occupies one line nand is output in increasing order of ; if it is still not unique, it Ais output in increasing order of . If the solution does not exist, it is output on one line No Solution.

Input sample:

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


Case 1:

When I saw this question, the first thing I thought of was to use dfs backtracking pruning to solve violently. A total of three functions need to be customized

  1. isPrime(): Find prime numbers
  2. gcd(): find the greatest common divisor
  3. add(): Accumulate the sum of each person
  4. dfs(): backtracking pruning

Then accumulate the string s1 through dfs, such as k=4, try all the situations between 0000-9999, and finally judge the boundary conditions

  1. The greatest common factor is prime and greater than 2
  2. The addition of bits of s1 is equal to m

If the boundary conditions are met, the corresponding data will be printed.

#include<bits/stdc++.h>
using namespace std;
int N, k, m, n;
string s1;
bool flag;
bool isPrime(int a) {
	if (a == 0 || a == 1)return false;
	for (int i = 2; i <= sqrt(a); i++) {
		if (a % i == 0)return false;
	}
	return true;
}
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
int add(string s) {
	int sum = 0;
	for (int i = 0; i < s.length(); i++)
		sum += s[i] - '0';
	return sum;
}
void dfs(int step) {
	if (step == k) {
		int t = gcd(m, add(to_string(stoi(s1) + 1)));
		if (t > 2 && add(s1) == m && isPrime(t)) {
			printf("%d %s\n", add(to_string(stoi(s1) + 1)), s1.c_str());
			flag = true;
		}
		return;
	}
	for (int i = 0; i <= 9; i++) {
		s1 += i + '0';
		dfs(step + 1);
		s1.erase(s1.length() - 1);
	}
}
int main() {
	cin >> N;
	int i;
	for (i = 0; i < N; i++) {
		cin >> k >> m;
		s1 = "";
		flag = false;
		printf("Case %d\n", i + 1);
		dfs(0);
		if (!flag) cout << "No Solution\n";
	}

	return 0;
}

The following are the running results. Two test points timed out, but the basic logic is fine, so the algorithm needs to be optimized.


Case 2:

The first thing I thought of was to use a tepm to record the maximum length of the s1 string that can be used, for example, input (6, 44), when the length of s1 is 6, the sum of all bits must be equal to 44, if when s1=99999, the length of s1 It is only 5, but the accumulation of all s1 is already 45, so there is no need to try later (pruning), which can reduce the running time of the algorithm. At the same time, when judging the boundary conditions in dfs, some sentences are redundant, and the calculation is performed twice. , so it can also be stored in a temporary variable to avoid multiple calculations.

#include<bits/stdc++.h>
using namespace std;
int N, k, m, n;
int tmpm;
string s1;
bool flag;
bool isPrime(int a) {
	if (a == 0 || a == 1)return false;
	for (int i = 2; i <= sqrt(a); i++) {
		if (a % i == 0)return false;
	}
	return true;
}
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
int add(string s) {
	int sum = 0;
	for (int i = 0; i < s.length(); i++)
		sum += s[i] - '0';
	return sum;
}
void dfs(int step) {
	if (step == k) {
		int n1 = add(to_string(stoi(s1) + 1));
		int t = gcd(m, n1);
		if (t > 2 && 0 == tmpm && isPrime(t)) {
			printf("%d %s\n", n1, s1.c_str());
			flag = true;
		}
		return;
	}
	for (int i = 0; i <= 9; i++) {
		s1 += i + '0';
		tmpm -= i;
		if (tmpm >= 0)
			dfs(step + 1);
		tmpm += i;
		s1.erase(s1.length() - 1);
	}
}
int main() {
	cin >> N;
	int i;
	for (i = 0; i < N; i++) {
		printf("Case %d\n", i + 1);
		cin >> k >> m;
		tmpm = m;
		if (k * 9 < m)
			cout << "No Solution\n";
		else {
			s1 = "";
			flag = false;
			dfs(0);
			if (!flag) cout << "No Solution\n";
		}
	}
	return 0;
}

Although the time-consuming of test point 0 is reduced, test points 2 and 3 are still timed out, so optimization ideas are still needed.


Case 3:

The idea of ​​optimization this time comes from the blog of the old handsome Bea . We ignored a very important point when reviewing the questions, that is, the greatest common factor of m and n must be greater than 2. It is easy to know that the unit of the final result of s1 must be 9. If it is not 9, then n=m+1, then the greatest common factor of n and m must only be 1, because the greatest common factor of two adjacent positive integers is 1, only the end of s1 is 9 or the end is Consecutive 9, after adding one, the end becomes 0 to make the greatest common factor of m and n greater than 2.

We also ignore a condition of the topic, that is, the results are arranged in ascending order according to the size of n, so use the vector group to store the results, and then sort and output the vector group to get the result.

#include<bits/stdc++.h>
using namespace std;
int N, k, m, n;
int tmpm;
string s1;
struct node {
	int n;
	string s;
}tmp;
vector<node> v;
bool cmp(node a, node b) {
	if (a.n != b.n) return a.n < b.n;
	else return a.s < b.s;
}
bool isPrime(int a) {
	if (a == 0 || a == 1)return false;
	for (int i = 2; i <= sqrt(a); i++) {
		if (a % i == 0)return false;
	}
	return true;
}
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
int add(string s) {
	int sum = 0;
	for (int i = 0; i < s.length(); i++)
		sum += s[i] - '0';
	return sum;
}
void dfs(int step) {
	if (tmpm < 0)
		return;
	if (step == k - 2) {
		s1 += "99";
		int n1 = add(to_string(stoi(s1) + 1));
		int t = gcd(m, n1);
		if (t > 2 && 0 == tmpm && isPrime(t)) {
			tmp.n = n1; tmp.s = s1;
			v.push_back(tmp);
		}
		s1.erase(s1.length() - 2);
		return;
	}
	for (int i = 0; i <= 9; i++) {
		s1 += i + '0';
		tmpm -= i;
		dfs(step + 1);
		tmpm += i;
		s1.erase(s1.length() - 1);
	}
}
int main() {
	cin >> N;
	int i, j;
	for (i = 0; i < N; i++) {
		printf("Case %d\n", i + 1);
		cin >> k >> m;
		tmpm = m - 18;
		if (k * 9 < m)
			cout << "No Solution\n";
		else {
			s1 = "";
			v.clear();
			dfs(0);
			if (v.size() == 0) cout << "No Solution\n";
			else {
				sort(v.begin(), v.end(), cmp);
				for (j = 0; j < v.size(); j++)
					printf("%d %s\n", v[j].n, v[j].s.c_str());
			}
		}
	}
	return 0;
}

The optimized algorithm can run through test points 2 and 3 and achieve AC.

Guess you like

Origin blog.csdn.net/qq_21891843/article/details/125082867