食用指南:
Leetcode专栏开启了,由于博主闭关期末,所以每日只能一题
尽量做到一题多解,先说思路,之后代码实现,会添加必要注释
语法或STL内容会在注意点中点出,新手友好
欢迎关注博主
神机百炼专栏,内涵算法基础详细讲解和代码模板
题目描述:
-
一个数分解为三个数之和,且分解结果中不能出现负数
-
题目来源:
道友提问
题目分析:
-
动态规划:后一个数的分解依靠前一个数的分解结果
第一个数可以分解为多种组合,
第二数的分解基于第一个数的后两个分解结果,
只要存在一种分解结果三个数都不为负,即可继续分解下一个数 -
输出:每次记录当前结果依赖于上一个数的哪种分解组合
若有多种只记录一种即可,毕竟若可以合法分解,最终一个数的分解组合必然可以倒推回第一种
且我们判断可否合法分解时将上个数的所有分解组合都审视过了
算法模板:
代码实现:
法一:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010;
int h[N];
int a[N], b[N], c[N], ne[N];
int idx;
int up[N]; //记录当前节点的来源
void add(int x, int num1, int num2, int num3, int y){
a[idx] = num1;
b[idx] = num2;
c[idx] = num3;
ne[idx] = h[x];
up[idx] = y;
h[x] = idx++;
}
void print(int idx){
if (up[idx] != -1){
print(up[idx]);
}
if(up[idx] == -1){
cout<<a[idx]<<" "<<b[idx]<<" "<<c[idx]<<" ";
return;
}
cout<<c[idx]<<" ";
}
int main(){
memset(h, -1, sizeof(h));
memset(up, -1, sizeof(up));
int n = 0;
cin >>n;
for(int i=0; i<n; i++){
int x = 0;
cin >> x;
if( i == 0){
for(int j=0; j<=x; j++){
for(int k=0; k<=x; k++){
if (x - j - k >= 0){
add(i,j,k,x-j-k, -1);
}
}
}
}else{
int flag = 0; //当该层有组成时就置为1
for(int j = h[i-1]; j!=-1; j=ne[j]){
if(x - b[j] - c[j] >= 0){
add(i, b[j], c[j], x-b[j]-c[j],j);
flag = 1;
}
}
if (flag == 0) {
cout<<"No"<<endl;
return 0;
}
}
}
cout<<"Yes"<<endl;
print(idx-1);
return 0;
}
注意点:
-
输出路径:
本题的路径只有走到终点时才明确,且当走到终点时不能逆推回起点类似这样的单向路径记录,我们最好使用up[]记录每个节点的来源节点,让记录路径不额外耗时