The K−P factorization of a positive integer N is to write N as the sum of the P-th power of K positive integers. You are supposed to write a program to find the K−P factorization of N for any positive integers N, K and P.
Input Specification:
Each input file contains one test case which gives in a line the three positive integers N (≤400), K (≤N) and P (1<P≤7). The numbers in a line are separated by a space.
Output Specification:
For each case, if the solution exists, output in the format:
N = n[1]^P + ... n[K]^P
where n[i]
(i
= 1, …, K
) is the i
-th factor. All the factors must be printed in non-increasing order.
Note: the solution may not be unique. For example, the 5-2 factorization of 169 has 9 solutions, such as 122+42+22+22+12, or 112+62+22+22+22, or more. You must output the one with the maximum sum of the factors. If there is a tie, the largest factor sequence must be chosen – sequence is said to be larger than if there exists such that for and .
If there is no solution, simple output Impossible
.
Sample Input 1:
169 5 2
Sample Output 1:
169 = 6^2 + 6^2 + 6^2 + 6^2 + 5^2
Sample Input 2:
169 167 3
Ω
给定 ,将 分解为 ,满足 。
一开始没什么头绪,就想着枚举呗,但从哪开始枚举呢。
首先题目要求如果有多个解则优先选择 更大的解,如果 也相等则选择第一个不同factor更大的解,我们也认为这个解>另一个解。为了可以少判断一个选择条件,我们直接从最大解开始遍历,这样先遇到的可行解就肯定是更大的。首先可以肯定 ,因此我们可以将 作为最大的初始(不一定可行)解向量,然后向下遍历。
我们规定 表示解向量中最后一个非零元素的引用, 表示解向量中的非零元素的个数, , , 表示目前最大的s, 表示将v的第一个0元素改为t, 表示将下一个可行解赋给 ,然后制定如下遍历规则:
-
-
:更新最优解, ,
:
:
-
-
:
:
简单地说,如果没到K个factor就继续加入最大可能的factor ,到了K个就看factor的P方和是否为N,若m=0那么就看这个解是否更优,s更大的话就更新最优解。
主体部分已经确定了,那么 应该如何实现呢。直接的想法就是对最后一个非零元素 减1,⚠️如果减到0了就必须对再前一个factor减1,否则将会重新遍历这段过程,陷入死循环。但我们还可以做出一些优化。首先,注意到在 的时候肯定会执行 ,此时最后一个元素 不管减多少肯定 ,不可能产生可行解,因此可以直接把 删除,对倒数第二个factor减1。而 时就不能直接把 删除,就执行正常减1。同时别忘更新 。
至此就差不多了,接下来做一些优化。注意到这种题目中会用到很多pow函数,但pow函数效率令人堪忧,一种常见的办法是自己实现一下简单的myPow函数,但是这一题我们注意到 ,因此我们只会求 以内整数的P次方,那么就可以直接打表,用一个vector存储1- 的P次方即可。另外当 时就说明接下来所遍历的解不可能产生可行解,直接跳过,而我们可以用之前的P次方vector外加二分查找快速找到 。
🐎
#include <iostream>
#include <vector>
using namespace std;
int n, k, p, s, s_max = 0, m;
vector<int> power{0, 1};
inline int nth_root(int a)
{
int left = 1, right = power.size(), mid;
while (left < right - 1)
{
mid = (left + right) / 2;
if (power[mid] > a) right = mid;
else left = mid;
}
return left;
}
// eliminate the last factor and minus 1
inline void next(vector<int> &sol)
{
s -= sol.back(), m += power[sol.back()];
sol.pop_back();
while (!sol.empty() && sol.back() == 1)
{
m += 1, s -= 1;
sol.pop_back();
}
while (!sol.empty())
{
sol.back() -= 1, s -= 1;
m += power[sol.back() + 1] - power[sol.back()];
if (power[sol.back()] * (k - sol.size()) < m)
{
m += power[sol.back()];
s -= sol.back();
sol.pop_back();
}
else break;
}
}
int main()
{
scanf("%d %d %d", &n, &k, &p);
for (int i = 2; power.back() < n; ++i)
{
int otc = i;
for (int j = 1; j < p; ++j) otc *= i;
power.push_back(otc);
}
vector<int> opt, tmp{nth_root(n)};
m = n - power[tmp.back()];
s = tmp.back();
while (!tmp.empty())
{
if (tmp.size() == k)
{
if (m == 0 && s > s_max)
{
s_max = s;
opt = tmp;
}
next(tmp);
}
else if (m > 0)
{
tmp.push_back(min(tmp.back(), nth_root(m)));
m -= power[tmp.back()];
s += tmp.back();
}
else
{
tmp.push_back(0);
next(tmp);
}
}
if (s_max == 0) printf("Impossible");
else
{
printf("%d = %d^%d", n, opt[0], p);
for (int i = 1; i < opt.size(); ++i)
printf(" + %d^%d", opt[i], p);
}
}