問題鏈接: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;
}