问题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805364711604224
题意:
给定正整数N, K, P,将N表示成K个正整数(可以相同,递减排序)的P次方的和,即N = n1^P+...nk^p。如果有多种方案,那么选择底数和n1+n2+...nk最大的方案;如果还有多种方案,那么选择底数序列的字典序最大的方案。
思路:
- 由于P大于1,并且在单词运行中是固定的,因此开一个vector<int> fac,在输入P之后就预处理出所有不超过N的n的P次方。即对N= 10,P = 2来说,fac[0] = 0, fac[1] = 1, fac[2] = 4, fac[3]=9...
- DFS函数用于从fac中选择若干个数(可以重复选),使得它们的和等于N。于是需要针对fac中的每个数,根据选与不选这个数来进入两个分支,因此DFS的参数中必须有:(1)当前处理到的是fac的几号位,记为index;(2)当前已经选择了几个数,记为nowK。(3)当前选择出的数之和sum。(4)为了保证有多个方案时底数之和最小,还需在参数中记录当前选择出的数的底数之和facSum。————另外开一个vector<int> ans,用来存放最优的底数序列,用一个vector<int> temp存放当前选中的底数组成的临时序列。
- 为让结果能保证字典序大的序列优先被选中,让index从大到小递减来遍历,这样就总是能选中fac中较大的数
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
vector<int> fac, ans, temp;
int N, K, P, maxfacsum = 0;//maxfacsum记录最大底数之和
//预处理fac数组
void init(){
int i = 1, temp = 0;
while(temp <= N){//当i的P次方没有超过n时,不断把i的P次方加入fac
fac.push_back(temp);
temp = pow(i, P);
i++;
}
}
//DFS函数,当前访问fac[index],nowK为当前选中个数
//sum为当前选中的数之和,facsum为当前选中的底数之和
void DFS(int index, int nowK, int sum, int facsum){
if(nowK == K && sum == N){
if(facsum > maxfacsum){
ans = temp;//更新最优底数序列,vector容器之间赋值
maxfacsum = facsum;//更新最大底数和
}
return;
}
if(sum > N || nowK > K)
return;
if(index - 1 >= 0){//fac[0]不需要选择
temp.push_back(index);//把底数index加入临时序列temp
DFS(index, nowK + 1, sum + fac[index], facsum + index);//选的分支
temp.pop_back();//选的分支结束把刚加进去的数pop掉
DFS(index - 1, nowK, sum, facsum); //不选的分支
}
}
int main(){
cin >> N >> K >> P;
init();
DFS(fac.size() - 1, 0, 0, 0);
if(maxfacsum == 0)
cout << "Impossible"<<endl;
else{
printf("%d = %d^%d", N, ans[0], P);
for(int i = 1; i < ans.size(); i++)
printf(" + %d^%d", ans[i], P);
}
return 0;
}