pat a 1103 integer factorization

本文通过一道具体的编程题解析了如何使用因数分解和字典序进行方案优化和选择,对比了作者初版代码与标准答案的差异,并详细分析了标准答案的思路和技术要点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生词:
factorization 因数分解;P-th power p次幂;tie 平局,两种方案相同;
sequence { a1, a2, … aK } is said to be larger than { b1, b2, … bK } if there exists 1<=L<=K such that ai=bi for ibL
这句是按字典序排序的意思, { a1, a2, … aK }比 { b1, b2, … bK }大就是指{ a1, a2, … aK }的字典序比 { b1, b2, … bK }大。

题目差点没读懂。读到输出格式倒是大致明白了。第一反应不会,但是先试试。
惨不忍睹,编译不过的第一版:

#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm> 
using namespace std;
vector<int> s,smax;
int basummax=0;
int N,K,P;
/*标答的思路是预先生成需要的平方数数列,因此只需要从大到小递归即可实现递减排序*/
bool cmp(int a,int b){
    return a>b;
} 
//不要在找到一组解后才判断字典序关系,应该让index从大到小选择
vector<int> zidianxu(vector<int> s,vector<int> smax){
    vector<int> res; 
    /*正确写法:
    vector<int>::iterator it=s.begin();
    sort(it,it+s.size(),cmp);*/
    sort(s,s+s.size(),cmp);//错误!
    sort(smax,smax+smax.size(),cmp);//错误!
    for(int i=0;i<s.size();i++){
        if(s[i]==smax[i]){
            continue;
        }else{
            s[i]>smax[i]?res=s:res=smax;
            break;
        }

    }
    return res; 

}
void DFS(int index, int sum){
    if(index>K)return;
    if(sum>N)return;
    if(sum==N&&index==K){
            int basum=0;//底数和
/*假如有5个方案,计算底数和的时间复杂度是O(K)(取时间复杂度最大的步骤的复杂度),容易超时。如果把底数和作为一个参数,则复杂度为O(1)*/           
            for(vector<int>::iterator it=s.begin();it!=s.end();it++){
              basum+=*it;
                } 
        if(basum>basummax){
            smax=s;//vector间可以直接赋值 
        }else if(bsum==basummax){
            smax=zidianxu(s,smax);
        } else{
            return;
        }
    }
    /*我的想法是N=400,P最小=2的情况时,底数选取的范围最大:1~20,可覆盖所有情况,但这样时间复杂度极高。标答的思路是当输入N,P时,将所有小于N的平方数计算并存储结果,然后从中选K个。另一个错误是s[index]中的数可以相同,我没有枚举这种情况*/
    for(int i=1;i<20;i++){
        s[index]=i;//vector不可以这样赋值,只能用push_back()!
        /*假如有5个方案,计算底数和的时间复杂度是O(K)(取时间复杂度最大的步骤的复杂度),如果把底数和作为一个参数,则复杂度为O(1)*/
        DFS(index+1,sum+pow(i,P));
    /*分析递归的时候分析1:递归有几个参数;2.递归的时候有几个分支,分支参数的变化。*/    
        continue;
    }

}
int main(){
    scanf("%d %d %d",&N,&K,&P);
    DFS(0,0);
    if(smax.empty()){
        printf("Impossible");
    } else{
        printf("%d =",N);
        for(int i=0;i<K;i++){
            printf(" %d^%d",smax[i],P);
            if(i<k-1){
                printf(" +");
            }
        }
    }
    return 0;
}

把自己的想法尽快用代码表达出来,反正通过的可能几乎没有。之所以自己先写一遍,是为了尽可能多的找到自己的薄弱点,虽然从头到脚都是弱点。至少if-else,for(){输出}我还是能写好的。
下面是标答:

#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
int n,k,p,maxFacSum=-1;//最大底数和 
vector<int> fac,ans,temp;//预处理的平方数,最优底数序列,当前底数序列 
/*int power(int x){
    int ans=1;
    for(int i=0;i<p;i++){
        ans*=x;
    }
    return ans;
}*/ 
//预处理fac数组 
void init(){
    int i=0,temp=0;
    while(temp<=n){
        fac.push_back(temp);
        temp=pow(++i,p);
    } 
}
void DFS(int index,int nowK,int sum,int facSum){
    if(sum==n&&nowK==k){
        if(facSum>maxFacSum){
            ans=temp;
            maxFacSum=facSum;
        }
        return;
    }
    if(sum>n||nowK>k)return;
    if(index-1>=0){
        temp.push_back(index);
        DFS(index,nowK+1,sum+fac[index],facSum+index);
        temp.pop_back();
        DFS(index-1,nowK,sum,facSum); 
    }
} 
int main(){
    scanf("%d%d%d",&n,&k,&p);
    init();
    DFS(fac.size()-1,0,0,0);
    if(maxFacSum==-1)printf("Impossible\n");
    else{
        printf("%d = %d^%d",n,ans[0],p);
        for(int i=1;i<ans.size();i++){
            printf(" + %d^%d",ans[i],p);
        }
    }
    return 0;
}

标答的思路就是先生成备选平方数,然后再用DFS轮。
我的思路是想到那写到那,全部用最笨最暴力的枚举解决。
傻。
默写失败第一版,“运行超时”

#include<cstdio>
#include<vector>
using namespace std;//常忘1 
vector<int> powSes,tempRes,maxRes;//vector<int>常忘2 
int n,k,p,maxKSum=-1;
int power(int x){
    int ans=1;
    for(int i=0;i<p;i++){
        ans=ans*x;
    }
    return ans;
}
void init(){
    int temp=0;
    for(int i=0;;i++){//为了保证第i位的数据是i^p,所以第0位的数据是0 
        temp=power(i);
        if(temp>n)return;
        powSes.push_back(temp);
    }
}
void DFS(int index,int sum,int kSum,int kc){
    if(sum==n&&kc==k){
        if(kSum>maxKSum){
            maxRes=tempRes;
            maxKSum=kSum;
        }/*else if(kSum==maxKSum){
            for(int i=0;i<k;i++){
                if(maxRes[i]<tempRes[i]){
                    maxRes=tempRes;
                    break;
                }else if(maxRes[i]>tempRes[i]){
                    break;
                }
            }将次方数组从大到小选数,后面的满足条件的序列其字典序必然小于前面先满足条件的序列 
        }*/
        return; 
    }
    if(sum>n||kc>k)return;
    /*while(index-1>=0){//假设index是5,两条DFS子函数都执行完了,因为while(5-1>=0),故又重复执行一遍,死循环。 
        tempRes.push_back(powSes[index]);
        要保存的是底数序列,不是次方序列! 
        DFS(index,sum+powSes[index],kSum+index,kc+1);
        tempRes.pop_back();
        DFS(index-1,sum,kSum,kc);
    }*/
    if(index-1>=0){
        tempRes.push_back(index);
        DFS(index,sum+powSes[index],kSum+index,kc+1);
        tempRes.pop_back();
        DFS(index-1,sum,kSum,kc);

    }
}
int main(){
    scanf("%d %d %d",&n,&k,&p);
    init();
    DFS(powSes.size()-1,0,0,0);
    if(maxKSum==-1)printf("Impossible");
    else{
        printf("%d =",n);
        for(int i=0;i<k;i++){
            printf(" %d^%d",maxRes[i],p);
            if(i<k-1){
                printf(" +");
            }
            }       
        }
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值