PAT A1103 Integer Factorization (30 分)
The K−P factorization of a positive integer N is to write N as the sum of the P-th power of K positive integers. You are supposed to write a program to find the K−P factorization of N for any positive integers N, K and P.
Input Specification:
Each input file contains one test case which gives in a line the three positive integers N (≤400), K (≤N) and P (1<P≤7). The numbers in a line are separated by a space.
Output Specification:
For each case, if the solution exists, output in the format:
N = n[1]^P + ... n[K]^P
where n[i] (i = 1, ..., K) is the i-th factor. All the factors must be printed in non-increasing order.
Note: the solution may not be unique. For example, the 5-2 factorization of 169 has 9 solutions, such as 122+42+22+22+12, or 112+62+22+22+22, or more. You must output the one with the maximum sum of the factors. If there is a tie, the largest factor sequence must be chosen -- sequence { a1,a2,⋯,aK } is said to be larger than { b1,b2,⋯,bK } if there exists 1≤L≤K such that ai=bi for i<L and aL>bL.
If there is no solution, simple output Impossible.
Sample Input 1:
169 5 2
Sample Output 1:
169 = 6^2 + 6^2 + 6^2 + 6^2 + 5^2
Sample Input 2:
169 167 3
Sample Output 2:
Impossible
实现思路:
这题是一道很好的题,对于这题的dfs写法我也采用了两种方式,而且最终两种方法的时间效率差不多都在200ms多(最佳情况下),这里有几个细节点,优先将每个数i的次方值存入一个数组,如果在递归时候每次调用pow函数计算次方值,会使得算法整体时间多了一倍,第二点在递归的时候要使用剪枝法,使得多余的一层递归可以省去,虽然答案不唯一,但是要求序列底数总和越大越好,若相等则找序列单个元素较大的一个,我们只需要从sqrt(N)来开始寻找底数即可,之所以底数从sqrt(N)开始是因为P是大于1的最小是2。
AC代码
方法一:
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int N,K,P,maxVal=-1;
vector<int> sq,ans,powKey;
void dfs(int idx,int sum,int p,int cnt) {//idx:底数 sum:所有数次方和 p:次方 cnt:底数和
if(sum==N) {
if(sq.size()==K&&cnt>maxVal) {//当满足是K个底数组成的和为N且底数和大于之前序列时
maxVal=cnt;
ans=sq;
}
return;
}
if(idx>=1) {//底数的下界
sq.push_back(idx);
if(sum+powKey[idx]<=N&&sq.size()<=K) {//剪枝 当sum+当前数的平方小于K并且当前这个数不是第K个以上的数才进入下一层递归
dfs(idx,sum+powKey[idx],p,cnt+idx);//选择当前的数
}
sq.pop_back();
dfs(idx-1,sum,p,cnt);//不选择当前的数
}
}
int main() {
cin>>N>>K>>P;
powKey.resize(N+1);//存储1~N每个数的P次方值
for(int i=0; i<=N; i++)//提前算好每个数的平方,可以使得时间花费减少一半
powKey[i]=pow(i,P);
dfs(sqrt(N),0,P,0);
if(maxVal==-1) printf("Impossible");
else {
cout<<N<<" = ";
for(int i=0; i<K; i++) {
printf("%d^%d",ans[i],P);
if(i<K-1) printf(" + ");
}
}
return 0;
}
方法二:
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
int N,K,P,maxVal=-1;
vector<int> sq,ans,powKey;
void dfs(int idx,int sum,int p,int cnt,int i) {//idx:底数 sum:所有数次方和 p:次方 cnt:底数和 i:sq数组的下标来存底数
if(i==K) {
if(sum==N&&cnt>maxVal) {
maxVal=cnt;
ans=sq;
}
return;
}
while(idx>=1) {
if(idx==0) return;
if(sum+powKey[idx]<=N) {//剪枝 当sum+下一个数平方<=N时才进入下一层递归
sq[i]=idx;
dfs(idx,sum+powKey[idx],p,cnt+idx,i+1);
}
idx--;//不选择这个数的时候--
}
}
int main() {
cin>>N>>K>>P;
powKey.resize(N+1);
sq.resize(N);
for(int i=0; i<=N; i++) powKey[i]=pow(i,P);
dfs(sqrt(N),0,P,0,0);
if(maxVal==-1) printf("Impossible");
else {
cout<<N<<" = ";
for(int i=0; i<K; i++) {
printf("%d^%d",ans[i],P);
if(i<K-1) printf(" + ");
}
}
return 0;
}
2.给定 N 个整数(可能有负数),从中选择 K 个数,使得这 K 个数之和恰好等于一个给定的整数 X;如果有多种方案,选择它们中元素平方和最大的一个。数据保证这样的方案唯一。例如,从 4 个整数 {2, 3, 3, 4} 中选择 2 个数,使它们的和为 6,显然有两种方案 {2, 4} 和 {3, 3},其中平方和最大的方案为 {2, 4},这种就是答案不唯一的情况。
void DFS(int index, int nowK, int sum, int sumSqu) {
if (nowK == k && sum == x) {//找到 k 个数的和为 X
if (sumSqu > maxSumSqu) {//如果比当前找到的更优
maxSumSqu = sumSqu;//更新最大平方和
ans = temp;//更新最优方案
}
return;
}
//已经处理完 n 个数,或者超过 k 个数,或者和超过 x,返回
if (index == n || nowK > k || sum > x) return;
//选 index 号数
temp.push_back(A[index]);
DFS(index + 1, nowK + 1, sum + A[index], sumSqu + A[index] * A[index]);
temp.pop_back();
//不选 index 号数
DFS(index + 1, nowK, sum, sumSqu);
}
这类型的题目都可以归纳为一类dfs+剪枝的题型