爆刷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<bits/stdc++.h>
#include<unordered_map>
using namespace std;
#define inf 409
#define INF 0x3f3f3f3f
#define loop(x,y,z) for(x=y;x<z;x++)

int n,k,p;
int ans_sum;
vector<int>ans(inf);
vector<int>e(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<k-pos)return;//凑不满的,别算了
    int upbound=(int)pow(rem,1.0/p);
    for(int i=downbound;myPow(i,p)<=rem;i++)
    {
        e[pos]=i;
        dfs(pos+1,i,rem-myPow(i,p),sum+i);
    }
}

int main()
{
    cin>>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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值