PAT甲級1103 Integer Factorization (30分) ***** 有難度 測試點2分析, 遞歸寫的再規範點

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

題目倒是好讀懂,關鍵是如何構建這個等式,我沒想到太好的辦法,就是準備用深搜試試,題目給出的N最大也就400,最初感覺良好,結果還是爲了時間優化了好久,另外這題可以好好整理下遞歸深搜的標準寫法。

先說測試點2吧,這個測試點的情況應該是 因子和相同的情況有兩種,打個比方一個情況是 6 6 後面跟着幾個數字, 6 5 後面跟着幾個數字,兩個都是滿足的,你需要輸出第一種情況,大家可以參考下。

用深搜的話,最直觀的思路是對每個子式的所有可能的數字都遍歷一遍(所有可能的是指從1 到maxLimit=pow(N,(double)1/P); N開P次方) ,樣例中的169 開2次方是13,169應該由5個[1,13]之間數的二次方相加構成的。

但是按照上面說的,如果按照K個位置,每個位置從1到maxLimit 深搜的話我寫的代碼時間會爆炸,有測試點過不了,而且對於樣例的那種情況會出現6 6 6 6 5,5 6 6 6 6 這種全排列的情況,這肯定是需要優化的,結果當時沒想到如何優化,後來看了看柳神的代碼,發現了優化思路。。。。。。。。小尷尬
柳神本題的博客

詳細看了下柳神代碼,爲了避免遇到全排列的情況,也是對每個位置的數字進行遞歸,但是遞歸時候多一個因子上限參數index,這個參數限制你在該位置數字的最大上限,巧妙地避免了遇到全排列的情況

比如樣例的那個169 5 2,在第一個位置的遞歸,index可以指向13 12 11 10 一直到1的平方,但是在第二個位置遞歸函數時候index的上限就是第一次index所指向的值,換句話說就是第二次遞歸的時候傳入的Index是12 那麼第二個位置只能考慮1 到12,第二次遞歸的時候傳入的index是9,那麼第二個位置只能考慮1到9的數字。 這樣感覺很巧妙實現了遞歸的數字都是降序排列的,不會遇到我最初想的那種排列的情況。

這裏總結下看完柳神題解的感覺吧

  • 我是習慣使用一個vector把所有的K個位置的數字放到vector裏面,然後深搜,深搜完畢後再pop,如下

    int pos=0;
    
    while(pow(factor[pos],P)<=num&&pos<factor.size())
    {
    
    
        solution.push_back(factor[pos]);
    
        df(num-pow(factor[pos],P),factorNum-1);
    
        solution.pop_back();
        pos++;
    }
    
    

    其實完全沒必要pop_back了,只要再加一個保存當前遞歸位置的遞歸變量就行,temkp記錄當前是放置第幾個位置就好了,到時候只需要考慮在temkp的位置放置那個數字,而不用考慮去除,後面遞歸時候會自己覆蓋

  • 然後就是構造一個Pow數組,不用每次計算Pow了

  • 還有就是把factorSum也當成一個遞歸的變量,而且柳神的遞歸方式確保了子式數字選擇的時候從大到小,這樣在最終結果判斷的時候只考慮factorSum比當前的大了,保存本次的結果,factorSum相等的時候,因爲因子從大到小判斷,所以第一次找到的最大和是字典序最大了的,不用改變,這思路,嘖嘖嘖

     if (tempK == k) {
    
            if (tempSum == n && facSum > maxFacSum) {
                    ans = tempAns;
                    maxFacSum = facSum;
            }
           
            return;
        }
    

我後面AC的代碼走了另一條路子
我們不從位置上看了,從每個可能數字的數量上考慮,題目中樣例是169 5 2,在結果的5個數中,每個數可能是1到13,那麼最終結果含有0個1 ,含有1個,兩個1,三個1,四個1,5個1(這個可以直接判斷和不爲169 return掉),這五種情況,每個還可以繼續對含有幾個2遞歸 ,可能有0個2 ,1個2 ,3個2,4個2 ,然後對3的個數進行遞歸,一直到13。

這樣時間還是不太能過,再優化,爲了更節省時間,從13 到1進行判斷,從含有幾個13,含有幾個11,到了看含有幾個1這一步時候,我們完全可以看剩餘的和 以及剩餘的子式數目來判斷了,更簡單,如果反過來先看含有幾個1就很蠻了,如果K是100,那麼在第一層含有幾個1的遞歸中,就需要進行100次,太多了。

然後爲了直接可以輸出最終結果,不對和相同的結果進行排序,我們在對含有幾個13遞歸的時候,先考慮含有最多的13,而不是先考慮含有1個13的情況,這樣可以確保我們首先得到的結果肯定是滿足字母序的,比如測試點2兩種情況,6 6 **** 和6 5 *** ,我們需要確保首先遞歸得到的是 6 6 開頭的結果,所以必須要先考慮最多12,也就是5個12的情況,然後考慮4個12的情況。

AC代碼
時間如如下
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int N,K,P;
vector<int> factor;
vector<int> out;
vector<int> solution;

int maxSum=-1;
int cmp(vector<int>a ,vector<int> b)
{
    for(int i=0; i<a.size(); i++)
    {
        if(a[i]!=b[i])
            return a[i]>b[i];
    }
}


void df(int sum,int factorNum,int pos)
{
    if(factorNum==0&&sum==0)
    {
        int cacheSum=0;
        for(int i=0; i<solution.size(); i++)
        {
            cacheSum+=solution[i];
        }
        if(cacheSum>maxSum)
        {
            maxSum=cacheSum;
            out=solution;

        }
        return ;
    }
    else if(factorNum==0||sum<factorNum||sum<=0||pos>=factor.size())
        return ;

    //樣例之中就是 計算12^2次方
    int getnum=pow(factor[pos],P);

    //169最多含有幾個12^2 ,這個數字其實還要和當前可以有的子式數目比較下factorNum
    //也是爲了加快速度,比如169 最多有42個2^2  而factorNum最多也就5個,我們應該說
    //這裏最多也就5個2了
    int maxNum=sum/getnum;
	

    if(maxNum>factorNum)
        maxNum=factorNum;
     	//這裏就是先壓入最多可能的因子,後面再一個個彈出
    for(int i=1; i<=maxNum; i++)
        solution.push_back(factor[pos]);
	//這裏就是先考慮 5個13 ,彈出一個13,考慮4個13,彈出一個13,考慮3個13,直到沒有13這個數字
    for(int i=maxNum; i>=0; i--)
    {

        df(sum-getnum*i,factorNum-i,pos-1);
         if(i>0)
            solution.pop_back();
    }





}
int main()
{

    cin>>N>>K>>P;
    int maxLimit=pow(N,(double)1/P);
    for(int i=1; i<=maxLimit; i++)
    {
        factor.push_back(i);
    }
    //factor.size()-1 是說先從可選擇的最大的子式進行,不如169 最大是12^2
    //12在factor裏面的位置就是 factor.size()-1
    df(N,K,factor.size()-1);
    if(out.size()>0)
    {
        //sort(out.begin(),out.end(),cmp);
        printf("%d = %d^%d",N,out[0],P);
        for(int q=1; q<out.size(); q++)
        {
            printf(" + %d^%d",out[q],P);
        }
    }
    else
        cout<<"Impossible";

    return 0;
}

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