「1103」Integer Factorization

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值