*PAT_甲级_1103 Integer Factorization (30point(s)) (C++)【DFS进阶用法】

目录

1,题目描述

题目大意

2,思路

数据结构

算法

3,AC代码

4,解题过程

第一搏

第二搏

第三搏


1,题目描述

Sample Input 1:

169 5 2

 

Sample Output 1:

169 = 6^2 + 6^2 + 6^2 + 6^2 + 5^2

 

Sample Input 2:

169 167 3

 

Sample Output 2:

Impossible

题目大意

将一个数N分解成K个因子的P次方之和,要求当结果不唯一时,取因子之和最大的,仍不唯一,则将序列按照从大到小排序,选择较大序列(从第一个数往后,直到当前位置的数组A中的数大于数组B中的数;

不存在这样的分解方法时,输出Impossible;

 

2,思路

参考大神的解法:@日沉云起【pat甲级1103. Integer Factorization (30)】

(解题过程的第二搏第三搏中的思路均可通过测试,这里仅详细介绍最简解法)

数据结构

  • vector<int> tem:暂时存放结果序列;
  • vector<int> ans:结果序列;
  • vector<int> powValue:存储所有不超过N的n^P中的n;
  • DFS函数参数:start指向powValue的特定位置,dep深度(个数),facSum因子之和,powSum指数之和;

算法

  1. 对不超过N的n^P进行打表,并把结果存储到powValue中,DFS遍历时针对此表进行:
  2.  DFS设计:出口(dep >= K || powSum >= N,若满足dep == K && powSum == N && facSum > maxFacSum则更新ans);遍历(选择当前的start;不选择当前的start,而是更小的数)注意入栈/出栈的时机,以及递归的参数
  3. 函数调用及输出:

 

3,AC代码

(即第三搏中的代码)

#include<bits/stdc++.h>
using namespace std;
int N, K, P;//N给出的数 K因子个数 P因子的指数
vector<int> tem, ans, powValue;
int maxFacSum = 0;
void initPow(){     //下标对应因子 值对应因子的P次方
    powValue.push_back(0);
    while(powValue.back() < N)
        powValue.push_back(pow(powValue.size(), P));    // !!!妙啊
}
void dfs(int start, int dep, int facSum, int powSum){   //start指向powValue的特定位置 dep深度 facSum因子之和 powSum指数之和
    if(dep >= K || powSum >= N){
        if(dep == K && powSum == N && facSum > maxFacSum){
            maxFacSum = facSum;
            ans = tem;
        }
        return;
    }
    if(start >= 1){
        tem.push_back(start);
        dfs(start, dep+1, facSum+start, powSum+powValue[start]);    //powValue[start]即start的P次方
        tem.pop_back();
        dfs(start-1, dep, facSum, powSum);              //未选择当前的start 而是选择更小的数
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE

    scanf("%d %d %d\n", &N, &K, &P);
    initPow();
    dfs(powValue.size()-1, 0, 0, 0);                    //从大到小开始选择
    if(ans.size() == K){
        printf("%d = %d^%d", N, ans[0], P);
        for(int i = 1; i < ans.size(); i++)
            printf(" + %d^%d", ans[i], P);
    }else
        printf("Impossible");
    return 0;
}

 

4,解题过程

第一搏

想到了DFS,但是连样例都没运行出来。。。

#include<bits/stdc++.h>
using namespace std;
int N, K, P;//N给出的数 K因子个数 P因子的指数
vector<int> tem, ans;
bool cmp2(int a, int b) {return a > b;}
bool cmp1(vector<int>& A, vector<int>& B){
    int sumA = 0, sumB = 0;
    if(B.size() == 0) return true;
    for(int i = 0; i < K; i++){
        sumA += A[i];
        sumB += B[i];
    }
    if(sumA != sumB) return sumA > sumB;
    sort(A.begin(), A.end(), cmp2);
    sort(B.begin(), B.end(), cmp2);
    for(int i = 0; i < K; i++)
        if(A[i] != B[i])
            return A[i] > B[i];
}

void dfs(int start, int dep, int sum){
    tem.push_back(start);
    sum += (int)pow(start, P);
//    if(sum > N){
//        tem.pop_back();
//        return;
//    }
    if(dep >= K){
        //printf("YES1\n");
        if(sum == N){
            for(int i = 0; i < tem.size(); i++)
                printf(" %d", tem[i]);
            printf("\n");
            if(cmp1(tem, ans))
                ans = tem;
        }

        //printf("YES2\n");
        tem.pop_back();
        return;
    }
    for(int i = start; i <= 20; i++){//P最小为2 N最大为400 20^2=400
        dfs(i, dep+1, sum);
        //tem.pop_back();
    }
    tem.pop_back();
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE

    scanf("%d %d %d\n", &N, &K, &P);
    for(int i = 1; i <= 20; i++)
        dfs(i, 1, 0);
    //cout<<ans.size()<<endl;
//    for(int i = 0; i < ans.size(); i++)
//        printf(" %d", ans[i]);
    return 0;
}

看着自己写的代码,都感觉时间复杂度要炸开了。。。

 

第二搏

向大神求助,,,

不愧是大神,逻辑简单但十分准确到位:

#include<bits/stdc++.h>
using namespace std;
int N, K, P;//N给出的数 K因子个数 P因子的指数
vector<int> tem, ans;
int maxFacSum = 0;
void dfs(int start, int dep, int facSum, int powSum){//start起点 dep深度 facSum因子之和 powSum指数之和
    if(dep >= K || powSum >= N){
        if(dep == K && powSum == N && facSum > maxFacSum){
            maxFacSum = facSum;
            ans = tem;
        }
        return;
    }
    if(start >= 1){
        tem.push_back(start);
        dfs(start, dep+1, facSum+start, powSum+pow(start*1.0, P*1.0));//选择这个start !!!这里的pow函数不要强制转换为int 会出错
        tem.pop_back();
        dfs(start-1, dep, facSum, powSum);//未选择当前的start 而是选择更小的数
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE

    scanf("%d %d %d\n", &N, &K, &P);
    dfs((int)pow(N*1.0, 1.0/P)+1, 0, 0, 0);//从P次根号下N从大到小进行选择; +1保证强制转换后不会漏掉数; 
    if(ans.size() == K){
        printf("%d = %d^%d", N, ans[0], P);
        for(int i = 1; i < ans.size(); i++)
            printf(" + %d^%d", ans[i], P);
    }else
        printf("Impossible");
    return 0;
}

 

对一些必要的地方进行了剪枝:

  • 对因子的最大值进行了限制:                                                                                                                    
  • 对因子的最大个数dep,和指数之和powSum进行剪枝:                                                             

精妙之处:

  • 使递归从最大的数开始,这样可以避免当因子之和相等时,一个个比较序列中的数字;
  • 适当剪枝;

有种子弹从耳朵👂边飞过的感觉。。。Σ(っ °Д °;)っ

第三搏

打表法!

对不超过N的n^P进行打表,并把结果存储到powValue中,从而避免重复计算数字i的P次方。

每次从powValue中取值:下标即因子,值即因子对应的P次方;

#include<bits/stdc++.h>
using namespace std;
int N, K, P;//N给出的数 K因子个数 P因子的指数
vector<int> tem, ans, powValue;
int maxFacSum = 0;
void initPow(){     //下标对应因子 值对应因子的P次方
    powValue.push_back(0);
    while(powValue.back() < N)
        powValue.push_back(pow(powValue.size(), P));    // !!!妙啊
}
void dfs(int start, int dep, int facSum, int powSum){   //start指向powValue的特定位置 dep深度 facSum因子之和 powSum指数之和
    if(dep >= K || powSum >= N){
        if(dep == K && powSum == N && facSum > maxFacSum){
            maxFacSum = facSum;
            ans = tem;
        }
        return;
    }
    if(start >= 1){
        tem.push_back(start);
        dfs(start, dep+1, facSum+start, powSum+powValue[start]);    //powValue[start]即start的P次方
        tem.pop_back();
        dfs(start-1, dep, facSum, powSum);              //未选择当前的start 而是选择更小的数
    }
}
int main(){
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE

    scanf("%d %d %d\n", &N, &K, &P);
    initPow();
    dfs(powValue.size()-1, 0, 0, 0);                    //从大到小开始选择
    if(ans.size() == K){
        printf("%d = %d^%d", N, ans[0], P);
        for(int i = 1; i < ans.size(); i++)
            printf(" + %d^%d", ans[i], P);
    }else
        printf("Impossible");
    return 0;
}

注意:在某些编译环境中,pow函数的取值可能会产生误差。比如代码在codeBlock上运行时,针对样例输出(第二搏中的代码,也出现了这样的问题,但是把强制转换去掉后,莫名其妙就OK了)

但是,在PTA平台上提交,没有任何问题:

贼稳!o(* ̄▽ ̄*)ブ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值