题意:给一个数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;
}