爆刷PAT(甲級)——之【1103】 Integer Factorization (30 分)——DFS+剪枝

題意:給一個數N(小於400),項數K(小於P),次方P——把數N分解爲K項P次方的和的形式。輸出時候把係數大的先輸出。如果答案有多個,那麼選系數和最大的那個;如果還是有多種情況,那麼係數更大的那種。(係數必然爲正數

難點:本題就是卡時間。雖然N不會太大,但是K和N一樣大,而每一位都有T=pow(N,1/P)的可能,就會爆掉。

思路:1、如果到達K項,所有項加起來不到N肯定不是答案

2、對於K位的每一位的係數遍歷過程,都是由上界的,就是N減去前面定好的係數的項後,剩下的餘數rem的開P次根號

由於題目要求輸出是選擇係數最大的先。所以各項的係數在遍歷的時候從上界遞減順序來進行遍歷。

到這裏交上去還是有兩個點超時。

發現一個問題。如果第一項係數是5的P次方,後面出現一個1的P次方的話,就和 “第一項是1的P次方,後面出現5的P次方”重複了。沒想好怎麼剪枝這一塊。於是我以爲係數的下界是(剩下的餘數rem的開P次根號)的開P根號或除2,但都不正確

3、看到一種方法不錯,係數從下界開始往上界遍歷,如果是i的話,下一位的係數下界就是從i開始,i之前的不用遍歷的。這樣就可以解決上述將的重複問題。但是由於輸出的時候要將最大的係數先輸出,所以最後輸出的時候是倒序輸出。

完成這三個部分的剪枝以後還只剩下最後一個點難以通過,想必是十分大的運算量。

4、想來想去是C++的pow函數效率太低了。於是手寫一個快速冪代替掉這個pow。提交AC,但還是一般般,時間要求1200ms,我的耗時1100ms。。。

看了一下柳神等人的代碼,原來是先將 i 的各P次方先打表出來了。。。

 

總結:像這種卡時間的題目做着還挺有意思的。

Code:

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define inf 409
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;x<z;x++)

int n,k,p;
int ans_sum;
vector<int>ans(inf);
vector<int>e(inf);
int flag=0;

int myPow(int x,int y)//快速冪
{
    if(!y)return 1;
    int z=myPow(x,y>>1);
    if(y%2)return z*z*x;
    return z*z;
}

void dfs(int pos,int downbound,int rem,int sum)
{
    if(pos==k)//終點
    {
        if(!rem)//餘數爲0
        {
            flag=1;
            if(sum>=ans_sum){ans_sum=sum;ans=e;}//相等也要更新,因爲pos的值i是升序更新
        }
        return;
    }
    if(rem<k-pos)return;//湊不滿的,別算了
    int upbound=(int)pow(rem,1.0/p);
    for(int i=downbound;myPow(i,p)<=rem;i++)
    {
        e[pos]=i;
        dfs(pos+1,i,rem-myPow(i,p),sum+i);
    }
}

int main()
{
    cin>>n>>k>>p;
    dfs(0,1,n,0);
    if(!flag)printf("Impossible\n");
    else
    {
        printf("%d = %d^%d",n,ans[k-1],p);
        int i;
        for(i=k-2;i>=0;i--)printf(" + %d^%d",ans[i],p);
        printf("\n");
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章