PAT(A) 1103 Integer Factorization (30)

  • 题目地址及大意
  • 原始思路及代码
  • 答案思路及代码

题目地址及大意

题目PAT(A) 1103大意如下:

输入n, k, p三个正整数,输出k个非0正整数的组合,使得每个数的p次方的和等于n。若这样的组合不存在,则输出Impossible,若组合存在且不唯一, 则输出和最大的序列,若存在和相等的情况,则输出从大到小排最大的序列。

原始思路及代码

在做这道题的时候,我首先想的是定义一个长度为k的数组,每个位置存放 n/kp , 设置front = 0, back = n-1, 指示下一次需要变更的数字的位置;而后计算 sum=k1i=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}, k1i=0vec[i]p<n ,最后一个位置的元素出栈,在确保放入比最后一个位置的元素大1的元素后,仍满足 k1i=0vec[i]p<n 的情况下,比原始最后一个位置的元素大1的元素入栈,得到{1,1,1,1,2}
{1,1,1,1,2}, k1i=0vec[i]p<n ,最后一个位置的元素出栈,在确保放入比最后一个位置的元素大1的元素后,仍满足 k1i=0vec[i]p<n 的情况下,比原始最后一个位置的元素大1的元素入栈,得到{1,1,1,1,3}
以此类推……
{1,1,1,1,12}, k1i=0vec[i]p<n ,最后一个位置的元素出栈,但若将13放入最后一个位置,则 k1i=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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值