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;
}


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: