解题过程的小记录,如有错误欢迎指出。
难度:五星(DFS选择满足条件的最优序列)
题目分析
找出k个数的p次方之和为n
注意点
- 最终要找的数列是满足一系列条件的的底数,次方和知识中间寻找所需要的条件
- 最好在开始的init中就找出所有的<=n的次方数,简化后续的运算
- 底数从大到小开始找,这样就省去了在出现tie的时候比较的情况,因为从大到小找的时候,先找出的满足的序列,一定是各个数更大的
- 在进行下一层搜索之前,先看所加在一起的和是否超过了n,如果超过了就没有必要进入到下一层,因为进入到下一层再判断的话,会更加费时
我的解题过程
思路
- 初始化数组Fac,用于保存<=n的底数为下标的p次方的数
- 设置temp数组保存过程中所用到的底数,ans数组保存满足题目条件的底数(也就是答案)
- 写DFS函数(看代码理解吧)
bug
- 自己写的时候没有init而且还是从小往大找,各种超时报错,死得很惨烈
- maxFacSum初始化值要设为-1,不然好像有一个测试点会报错,不过这种不参加运算(参加运算的是tempFacSum),只用比较然后直接赋值的都可以初始化为-1,方便后续的比较
代码
#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
vector<int> temp, ans, fac;//temp用来存储中间结果,ans用来返回最终结果,fac用来存储x的p次方的数
int n, k, p, maxFacSum = -1;
void init() {//把<=n的p次方的数都存储到fac中,对应的下标是底数
int temp = 0, index = 1;
while (temp <= n) {
fac.push_back(temp);
temp = pow(index, p);
index++;
}
}
void DFS(int index, int tempSum, int tempK, int facSum) {
if (tempK == k) {//当前的底数个数为所要求的k时
if (tempSum == n&&facSum > maxFacSum) {//当前的底数的p次方和为n,且底数和大于原先记录的最大和
ans = temp; //进行更新
maxFacSum = facSum;
}
return;
}
if (tempSum > n) return;//剪枝 当当前的底数和>n时 不再继续搜索
if (index >= 1) {//index为0的时候记录的是0的p次方=0不需要考虑
if (tempSum + fac[index] <= n) {//选入index
temp.push_back(index);//压入过程底数数组
DFS(index, tempSum + fac[index], tempK + 1, facSum + index);
temp.pop_back();//结束搜寻后退出数组
}
DFS(index - 1, tempSum, tempK, facSum);//不选入index
//if (index == 1) return;
//index--;//这个是柳神方法中的while所用到的
}
}
int main()
{
scanf("%d%d%d", &n, &k, &p);//要求k个数的p次方之和为n
init();
DFS(fac.size() - 1, 0, 0, 0);
if (ans.size() != 0) {
printf("%d =", n);
for (int i = 0; i < k; i++) {
printf(" %d^%d", ans[i], p);
if (i != k - 1) printf(" +");
}
}
else {
printf("Impossible");
}
return 0;
}
dalao的代码
全部代码因版权原因不放出来,大家可以自行去柳神博客购买或者参考晴神的上机笔记~
借鉴点
本篇看两位大佬的代码的话,只要看DFS部分即可,其它部分看自己
- 柳神的代码在DFS部分采用了while直到index不满足条件,这种方法会用时更少
- 晴神的代码在DFS部分采用了写两个DFS分别进行选和不选的递归,用时更多,但更易于理解