这题的测试点5从一直超时,然后慢慢改到400ms再改到200ms+,再到现在10几ms,真辛酸啊。。
思路:
1.基本想法就是枚举,暴力解,最开始用的循环做的枚举,后来发现利用递归去做更容易实现剪枝;
2.将[1,n-k+1]区间的整数的p-1次方用数组存储起来,将[1,(n-k+1)1/p]区间的整数的p次方用数组存储起来,以免之后用到时反复计算;
3.然后从下标1到k开始dfs,记录此次递归的下标index、历史次方和facSum、前一个数的大小last、历史算术和calSum,我们记up=max{(n-facSum-k+index)1/p,last},则下标为index的数,我们就应该从up遍历到1;
4.我们使用两个vector分别记录当前的测试数据和我们历史最佳结果,用sum记录历史最佳结果的算术和,若此次的测试结果按规则比历史最佳结果要好,则进行替换;
5.因为后面的数永远小于等于前面的数,因此若遍历到下标为index且index取值为i时,这趟遍历算术和最大的结果就是从index往后数字全为i,则和为(k-index+1)*i+calSum,如果它小于sum,那我们就进行剪枝,直接结束此趟遍历;
代码:
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
int n,k,p,sum=0;
vector<int> rv(1,0),v(1,0),rs,test;
void cmp(int calSum){
if(calSum>sum){
rs=test;
sum=calSum;
}
else{
for(int i=1;i<=k;i++)
if(test[i]<rs[i]) return;
else if(test[i]>rs[i]){
rs=test;
return;
}
}
}
void dfs(int index,int facSum,int last,int calSum){
int up=rv[n-facSum-k+index];
up=up>last?last:up;
for(int i=up;i>0;i--){
test[index]=i;
if(sum!=0&&(k-index+1)*i+calSum<sum) return;
if(index!=k) dfs(index+1,facSum+v[i],i,calSum+i);
else if(facSum+v[i]==n) cmp(i+calSum);
}
}
int main(){
scanf("%d%d%d",&n,&k,&p);
double rp=pow(p,-1);
rs.resize(k+1);
test.resize(k+1);
for(int i=1;i<=n-k+1;i++) rv.push_back(pow(i,rp));
for(int i=1;i<=rv[n-k+1];i++) v.push_back(pow(i,p));
dfs(1,0,rv[n-k+1],0);
if(rs[1]==0) printf("Impossible");
else{
printf("%d = %d^%d",n,rs[1],p);
for(int i=2;i<=k;i++) printf(" + %d^%d",rs[i],p);
}
return 0;
}