- 题目地址及大意
- 原始思路及代码
- 答案思路及代码
题目地址及大意
题目PAT(A) 1103大意如下:
输入n, k, p三个正整数,输出k个非0正整数的组合,使得每个数的p次方的和等于n。若这样的组合不存在,则输出Impossible,若组合存在且不唯一, 则输出和最大的序列,若存在和相等的情况,则输出从大到小排最大的序列。
原始思路及代码
在做这道题的时候,我首先想的是定义一个长度为k的数组,每个位置存放 n/k−−−√p , 设置front = 0, back = n-1, 指示下一次需要变更的数字的位置;而后计算 sum=∑k−1i=0num[i]p , 若 sum<n ,则将执行 num[front] ++,front ++, 否则执行 num[back] –, back–;但这种算法有一个测试点无法通过,并且证明是错误的。例如,输入样例1164 4 2, 输出结果为 Impossible, 但真正结果应为 1164 = 19^2 + 17^2 + 17^2 + 15^2。 虽然结果错误,但这里仍然贴出代码:
#include<iostream>
#include<math.h>
using namespace std;
int num[400];
bool flag = false;
void Output(int n, int k, int p, int front, int back)
{
if(num[k-1]<0)
return;
int sum = 0;
for(int i=0;i<k;i++)
sum += pow(1.0*num[i],1.0*p);
if(sum>n){
num[back] --;
back --;
}
else if(sum == n){
flag = true;
return;
}
else{
num[front] ++;
front ++;
}
if(front>back){
front = 0;
back = k-1;
}
Output(n, k, p, front, back);
return;
}
int main()
{
int n,k,p;
cin>>n>>k>>p;
int front;
int back;
int a;
double temp = 1.0*n/k;
double zhishu = 1.0/p;
a = pow(temp,zhishu);
for(int i=0;i<k;i++){
num[i] = a;
}
front = 0;
back = k-1;
Output(n, k, p, front, back);
for(int i=0;i<k;i++){
if(num[i] == 0){
flag = false;
break;
}
}
if(flag == false)
cout<<"Impossible";
else{
cout<<n<<" = "<<num[0]<<"^"<<p;
for(int i=1;i<k;i++)
cout<<" + "<<num[i]<<"^"<<p;
}
system("pause");
return 0;
}
答案思路及代码
答案采用暴力搜索的方式,使用了数的深度优先搜索。
按照题目规定,n<=400,则num数组如果存在,最大不能超过20,因此将从1-20的数的p次方存储在数组中,方便后续计算。
以题目中给的样例,169 5 2 如下,按照k=5, 每次搜索序列如下
{1,1,1,1,1},
∑k−1i=0vec[i]p<n
,最后一个位置的元素出栈,在确保放入比最后一个位置的元素大1的元素后,仍满足
∑k−1i=0vec[i]p<n
的情况下,比原始最后一个位置的元素大1的元素入栈,得到{1,1,1,1,2}
{1,1,1,1,2},
∑k−1i=0vec[i]p<n
,最后一个位置的元素出栈,在确保放入比最后一个位置的元素大1的元素后,仍满足
∑k−1i=0vec[i]p<n
的情况下,比原始最后一个位置的元素大1的元素入栈,得到{1,1,1,1,3}
以此类推……
{1,1,1,1,12},
∑k−1i=0vec[i]p<n
,最后一个位置的元素出栈,但若将13放入最后一个位置,则
∑k−1i=0vec[i]p>n
,此时需要改变倒数第二位的元素的大小,得到{1,1,1,2},此时需要将2入栈,再重复以上操作。
值得注意的是,用这种方法,确保了得到的数列是从小到大排列的,以下的代码借鉴了博客中“小双的做法”,代码非常简洁漂亮,运行速度也很快。
#include<iostream>
#include<vector>
using namespace std;
int num[21];
int MAX = -1;
vector<int> ans;
vector<int> vec;
void dfs(int start, int k, int n, int sum)
{
if(k == 0){
if(n == 0){
if(sum>=MAX){
MAX = sum;
ans = vec;
}
}
}
else{
if(n>0){
for(int i=start;i<21;i++){
if(n - num[i]<0)
break;
vec.push_back(i);
dfs(i, k-1, n-num[i], sum+i);
vec.pop_back();
}
}
}
}
int main()
{
int n,k,p;
cin>>n>>k>>p;
for(int i=1;i<21;i++){
num[i] = 1;
for(int j=0;j<p;j++){
num[i] *= i;
}
}
dfs(1, k, n, 0);
if(ans.empty())
cout<<"Impossible";
else{
cout<<n<<" = "<<ans[k-1]<<"^"<<p;
for(int i=k-2;i>=0;i--){
cout<<" + "<<ans[i]<<"^"<<p;
}
}
system("pause");
return 0;
}