zoukankan      html  css  js  c++  java
  • PAT1103

    1103. Integer Factorization (30)

    时间限制
    1200 ms
    内存限制
    65536 kB
    代码长度限制
    16000 B
    判题程序
    Standard
    作者
    CHEN, Yue

    The K-P factorization of a positive integer N is to write N as the sum of the P-th power of K positive integers. You are supposed to write a program to find the K-P factorization of N for any positive integers N, K and P.

    Input Specification:

    Each input file contains one test case which gives in a line the three positive integers N (<=400), K (<=N) and P (1<P<=7). The numbers in a line are separated by a space.

    Output Specification:

    For each case, if the solution exists, output in the format:

    N = n1^P + ... nK^P

    where ni (i=1, ... K) is the i-th factor. All the factors must be printed in non-increasing order.

    Note: the solution may not be unique. For example, the 5-2 factorization of 169 has 9 solutions, such as 122 + 42 + 22 + 22 + 12, or 112 + 62+ 22 + 22 + 22, or more. You must output the one with the maximum sum of the factors. If there is a tie, the largest factor sequence must be chosen -- sequence { a1, a2, ... aK } is said to be larger than { b1, b2, ... bK } if there exists 1<=L<=K such that ai=bi for i<L and aL>bL

    If there is no solution, simple output "Impossible".

    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
    这是一道很考验dfs能力的题目。思路很简单,找到所有的数i(i^p<n) 然后对这个数集进行dfs。
    我们来先看第一种写法
    void dfs(int index,int sum,int num,int factsum)
    {
        
        if(sum==n&&num==k)
        {
            
            if(factsum>maxsum)
            {
                maxsum=factsum;
                ans=temp;
            }
            else if(factsum==maxsum)
            {
    
                for(int i=0;i<temp.size();i++)
                {
                    if(temp[i]>ans[i])
                    {
                        ans=temp;
                        break;
                    }
                    else if(temp[i]<ans[i])
                        break;
                }
            }
            return;
        }
        if(num>k||sum>n)
            return;
        
        for(int i=index;i<=t;i++)
        {
            temp.push_back(v[i]);
    
            dfs(index+1,sum+w[i],num+1,factsum+v[i]); //每次允许重复的数字一定比第一个数字大,并且可以重复的次数与重复的数字有关。
            temp.pop_back();
    
        }*/
        return;
    }

    过了一部分样例,但是仔细分析发现这种方式重复的数字是有限制的,第一个数只能重复两次,后面的数如果想要重复必须比第一个数字大,并且重复的次数也有要求。

    例如对样例18 4 2就无法得到1 2 2 3因为比1大的数2最多只能重复(2-index)次,这里index为2

    再看下一种写法:

    void dfs(int sum,int num,int factsum)
    {   
        if(sum==n&&num==k)
        {
            if(factsum>maxsum)
            {
                maxsum=factsum;
                ans=temp;
            }
            else if(factsum==maxsum)
            {
    
                for(int i=0;i<temp.size();i++)
                {
                    if(temp[i]>ans[i])
                    {
                        ans=temp;
                        break;
                    }
                    else if(temp[i]<ans[i])
                        break;
                }
            }
            return;
        }
        if(num>k||sum>n)
            return;
        for(int i=1;i<=t;i++)
        {
            temp.push_back(v[i]);
    
            dfs(sum+w[i],num+1,factsum+v[i]); //每次允许重复的数字一定比第一个数字大,并且可以重复的次数与重复的数字有关。
            temp.pop_back();
    
        }
        return;
    }

    这种方法固然可行,但是肯定会超时。每次都重1开始计算,会出现大量重复的情况

    void dfs(int index,int sum,int num,int factsum)
    {
        if(sum==n&&num==k)
        {
            if(factsum>maxsum)
            {
                maxsum=factsum;
                ans=temp;
            }
            else if(factsum==maxsum)
            {
    
                for(int i=0;i<temp.size();i++)
                {
                    if(temp[i]>ans[i])
                    {
                        ans=temp;
                        break;
                    }
                    else if(temp[i]<ans[i])
                        break;
                }
            }
            return;
        }
        if(num>k||sum>n)
            return;
        if(index>=1)
        {
            temp.push_back(index);
            dfs(index,sum+w[index],num+1,factsum+v[index]);
            temp.pop_back();
            dfs(index-1,sum,num,factsum);
        }
        /*for(int i=index;i>=1;i--)
        {
            temp.push_back(v[i]);
    
            dfs(index-1,sum+w[i],num+1,factsum+v[i]); //每次允许重复的数字一定比第一个数字大,并且可以重复的次数与重复的数字有关。
            temp.pop_back();
    
        }*/
        return;
    }

    正解如上。从后往前递推,每个数字都可以重复多次,如果不符合条件就减1。同时这样相等时,第一个序列即为题目要求的输出。从前往后仍然可能会超时,因为初始的数字过于小了。

    网上题解:

    为了顺应题目找到最大系数和或者最大系数列,我们从小到大进行枚举,这样即使碰到了和相等的情况,由于是递增着枚举的,因此直接覆盖原来的系数列,得到的就是最终满足条件的系数列。

    我们利用DFS来从小到大的枚举,DFS函数的参数如下:

    dfs(long long N, int cur, vector<int>& factors);

    ①其中N是当前值,从最初的输入开始,逐步减去每个系数的运算结果;cur是枚举到的位置,从0开始,依次填入factors容器中,当cur==K时,枚举已经结束,我们判断N是否为0,为0则找到了一个满足条件的系数列,并且这个系数列按照升序存储在factors中。注意到cur==K但N≠0是我们的第一个剪枝条件。

    ②为了保证枚举从小到大开始,在每次枚举开始前计算lower和upper两个值,其中lower由factors中刚刚枚举完的上一个值确定,如果当前是枚举的起始点,则从1开始;upper为根号下N,因为系数的次方P>1,因此最大的系数不可能超过根号N。

    ③对lower到upper内的每一个值,计算系数的P次方,用res表示。如果N≥res,则说明合法,将其填入factors中并且向后枚举,即

     
    1. factors[cur] = i;  
    2. dfs(N-res,cur+1,factors);  


    如果N<res,说明系数偏大,又因为系数是递增枚举的,所以此后都不满足,直接返回,这是我们的第二个剪枝条件。

    综合上面两个剪枝条件,即可写出高效的dfs算法来枚举结果,为了能得到满足题目要求的系数列,我们设置全局变量nowSum和finalFactor,每次找到一个系数列,就求其系数和sum,如果sum≥nowSum,则更新nowSum与finalFactor,等号涵盖了题目中的第二个条件,因为后面出现的系数列一定大于前面出现的系数列(递增枚举)。

    最后如果finalFactor规模为K,则说明找到,先反转,后输出,注意格式;否则输出Impossible。

    这个题解是典型的剪枝方法

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long lint;
    
    lint N,K;
    int P;
    
    lint lpower(lint n, lint p){
        if(n == 1) return 1;
        int factor = n;
        for(int i = 1; i < p; i++) n *= factor;
        return n;
    }
    
    vector<int> finalFactor;
    int nowSum = 0;
    
    bool dfs(lint N, int cur, vector<int>& factors){
        if(cur == K){
            if(N == 0){
                int sum = 0;
                for(int i = 0; i < factors.size(); i++){
                    sum += factors[i];
                }
                if(sum >= nowSum){
                    finalFactor = factors;
                    nowSum = sum;
                }
                return true;
            }else return false;
        }
        lint upper = sqrt((double)N);
        lint lower = cur > 0 ? factors[cur - 1] : 1;
        for(lint i = lower; i <= upper; i++){
            lint res = lpower(i,P);
            if(N >= res){
                factors[cur] = i;
                dfs(N-res,cur+1,factors);
            }else{
                return false;
            }
        }
        return true;
    }
    
    int main()
    {
        cin >> N >> K >> P;
        vector<int> factors(K);
        dfs(N,0,factors);
        reverse(finalFactor.begin(),finalFactor.end());
        if(finalFactor.size() == K){
            printf("%d = ",N);
            printf("%d^%d",finalFactor[0],P);
            for(int i = 1; i < finalFactor.size(); i++){
                printf(" + %d^%d",finalFactor[i],P);
            }
        }else{
            cout << "Impossible";
        }
        cout << endl;
    
        return 0;
    }
  • 相关阅读:
    ASP.NET 身份验证机制
    ASP.NET 一般处理程序下载
    [c、c++]宏中"#"和"##"的用法(zz)
    XML 链接语言(XLink) 版本 1.0
    网上看到的对个人发展很不错的话
    【分享】WebForm中DataGrid的20篇经典文章
    Ajax
    ISO/OSI网络体系结构
    经典推荐.Net面试法宝(面试题收集)
    Union的问题
  • 原文地址:https://www.cnblogs.com/flightless/p/8995889.html
Copyright © 2011-2022 走看看