爆刷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
#include
using namespace std;
#define inf 409
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;xans(inf);
vectore(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>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;
}

 

你可能感兴趣的:(PAT甲级【爆刷】)