题意
给定目标数字n、组成数量k、次方数p,要求得到k个数字a[1]...a[k]
满足a[i] >= a[j](i < j)
且
n
=
a
[
1
]
p
+
.
.
.
+
a
[
k
]
p
n = a[1]^p + ... + a[k]^p
n=a[1]p+...+a[k]p 如果有tie,则取sum = a[1] + ... + a[k]
最小值对应解,如果仍有tie,则取vector<int> ans
最小解。
思路
首先很自然想到用DFS,但是顺手写下来就TLE了,管上限 测试点5 TLE,管下限 测试点0 TLE。
分析原因1
p最大 = 7,为了避免重复求pow(a, p)
,在DFS前算好要用的因子存到fact[idx]
,这样最多O(n) /= 7;
int fact[23], idx = 1;
while(fact[idx - 1] < n) fact[idx++] = pow(idx, p);
分析原因2
每次取因子进入tmp数组时,有些因子没必要考虑。
- 比如从1 - ptr中,对于pow(ptr1, p) * k < n的,不用进入下层循环,于是成了
[ptr1 + 1, ptr]
- 比如对于pow(ptr2, p) > n - k + 1的,当进入下层循环后,
n
n
e
x
t
=
n
−
p
o
w
(
p
t
r
2
,
p
)
<
k
−
1
n_{next} = n - pow(ptr2, p) < k - 1
nnext=n−pow(ptr2,p)<k−1 不能凑出剩下k - 1个数字,也不用进入下层循环。于是简化成了
[ptr1 + 1, ptr2 - 1]
while(ptr > 0){
if(fact[ptr] * k < n)break;
if(n - k + 1 >= fact[ptr]){
tmp.push_back(ptr);
DFS(n - fact[ptr], k - 1, sum + ptr, ptr);
tmp.pop_back();
}
ptr--;
}
分析原因3
每次找到目标后,对比此前找到的目标ans,和当前找到的目标tmp的过程需要O(k),而k有可能比较大。
常规比较方法:
int sum1 = 0, sum2 = 0;
for(int i = 0; i < k; i ++) sum1 += ans[i], sum2 += tmp[i];
if(sum1 < sum2) ans = tmp;
else if(sum1 == sum2){
int i = 0;
while(i < ans.size() && ans[i] == tmp[i]) i++;
if(i < ans.size() && tmp[i] > ans[i]) ans = tmp;
}
观察不难想到sum是不用重复求,用DFS传参进入下层循环即可。
那对比ans与tmp呢?不难发现在dfs过程中,同层元素要么从小到大,要么从大到小,假设从大到小,则每次第一次修改max_sum时对应的最优解。反之最后,代码如下:
if(k == 0){
if(max_sum < sum){
max_sum = sum;
ans = tmp;
}
return;
}
总结 - 剪枝
- 对于DFS,同一层往往有一个顺序,如此题可以是顺序取
[ptr, 1]
到下一层DFS(n - fact[ptr], k - 1, sum + ptr, ptr);
- 然后对如上所取的两边进行剪枝,剪枝方法是看目标ptr与其他参数之间的约束关系,比如此题用n、k和ptr的关系,从
[ptr, 1]
剪枝到[ptr2, ptr1]
- 为了方便理解逻辑,可以简单枚举前几层DFS的参数以观察规律,这里不难发现最有规律的是k,每下一层k–,并且约束着n不会成为负数,于是出循环条件为if(k == 0) return;
题解1
#include<bits/stdc++.h>
using namespace std;
int fact[23], idx = 1, n, k, p, max_sum;
vector<int> tmp, ans;
void DFS(int n, int k, int sum, int ptr){
if(k == 0){
if(max_sum < sum){
max_sum = sum;
ans = tmp;
}
return;
}
while(ptr > 0){
if(fact[ptr] * k < n)break;
if(n - k + 1 >= fact[ptr]){
tmp.push_back(ptr);
DFS(n - fact[ptr], k - 1, sum + ptr, ptr);
tmp.pop_back();
}
ptr--;
}
}
int main(){
cin>>n>>k>>p;
while(fact[idx - 1] < n) fact[idx++] = pow(idx, p);
DFS(n, k, 0, idx - 1);
if(!max_sum) puts("Impossible");
else{
printf("%d = ", n);
for(int i = 0 ; i < k ; i++)
printf("%d^%d%s", ans[i], p, i == k - 1 ? "\n" : " + ");
}
return 0;
}
题解2(从小到大找因子,结果一样20ms过测试点5)
#include<bits/stdc++.h>
using namespace std;
int fact[23], idx = 1, n, k, p, max_sum;
vector<int> tmp, ans;
void DFS(int n, int k, int sum, int ptr){
if(k == 0){
if(max_sum <= sum) max_sum = sum, ans = tmp;
return;
}
for(int i = 1; i <= ptr; i++){
if(fact[i] * k < n) continue;
else if(n - fact[i] >= k - 1){
tmp.push_back(i);
DFS(n - fact[i], k - 1, sum + i, i);
tmp.pop_back();
}else break;
}
}
int main(){
cin>>n>>k>>p;
while(fact[idx - 1] < n) fact[idx++] = pow(idx, p);
DFS(n, k, 0, idx - 1);
if(!max_sum) puts("Impossible");
else{
printf("%d = ", n);
for(int i = 0 ; i < k ; i++)
printf("%d^%d%s", ans[i], p, i == k - 1 ? "\n" : " + ");
}
return 0;
}
题目
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 { a1,a2,⋯,aK } is said to be larger than { b1,b2,⋯,bK } if there exists 1≤L≤K such that ai=bi for i<L and aL>bL.
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
Sample Output 2:
Impossible