题目
分析
题意
给定正整数N、K、P,将N表示成K个正整数(可以相同,递减排列)的P次方的和,即N=nP+…nkP。如果有多种方案,那么选择底数和n1+…+nK最大的方案,如果还有多种方案,那么选择底数序列的字典序最大的方案。
注意
多方案时判断是否更优的做法的时间复杂度最好是O(1),否则容易超时。因此必须在DFS的参数中记录当前底数之和facSum,避免在找到一组解时计算序列的底数之和。
代码及解析
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
vector<int> temp, final; //temp记录当前选择 final最终符合条件的选择
int n, k, p; //n 被分解的数 k 分解数的个数 p次方
int maxSum = 0, maxNum; //maxNum 可选择的最大数
//计算可选择的最大数
int calcNum()
{
int ret;
for (int i = 1; i < n; i ++ )
{
ret = 1;
for (int j = 0; j < p; j ++ )
{
ret *= i;
}
if(ret >= n)
return (i - 1); //该数的p次方是小于且最接近n的数
else
continue;
}
return -1;
}
// step 当前在第几个数 sum 当前总和 num 当前选择的数 facSum 记录目前选择的所有数总和
void dfs(int step, int sum, int num, int facSum)
{
if(step == k && sum == n) //到最后一个数且总和满足条件
{
// int s;
// for (int i = 0; i < static_cast<int>(temp.size()); i ++ )
// s += temp[i];
if(facSum > maxSum) //更新最大因数和(使用facSum取消循环优化时复)
{
maxSum = facSum;
final = temp; //记录结果
}
return;
}
// for (int i = num; i >= 1; i -- )
// {
// if(pow(i, p) + sum > n)
// continue;
//取消循环 使用num优化时复
if(sum > n || step > k) return; //不满足条件直接return
if(num - 1 >= 0) //边界条件 可选择的最小数为1
{
temp.push_back(num);
//dfs(step + 1, sum + pow(i, p), i);
dfs(step + 1, sum + pow(num, p), num, facSum + num); //选分支
temp.pop_back();
dfs(step, sum, num - 1, facSum); //不选分支 则更新num 保留step(相当于外循环)
}
// }
}
int main()
{
scanf("%d%d%d", &n, &k, &p);
maxNum = calcNum(); //计算可选择的最大数
if(maxNum == -1)
printf("Impossible\n");
dfs(0, 0, maxNum, 0); //搜
if(static_cast<int>(final.size()) == k) //满足条件时打印
{
printf("%d = ", n);
for (int i = 0; i < static_cast<int>(final.size()); i ++ )
{
printf("%d^%d", final[i], p);
if(i != k - 1)
printf(" + ");
}
printf("\n");
}
else
printf("Impossible\n");
return 0;
}