代码题--C++--最少立方数之和每个数最少可以写成几个数的立方和

代码题--C++--最少立方数之和每个数最少可以写成几个数的立方和


题目描述

给出一个数字N(0<N<1000000),将N写成立方数和的形式,求出需要的最少立方数个数。
例如N=17,1+8+8 = 17,最少需要3个立方数,则输出3。
N= 28,1+1+1+1+8+8+8=28, 需要7个立方数,1+27=28,需要2个立方数,所以最少立方数为2,则输出2。

输入描述:
一个数字N(0<N<1000000)
输出描述:
最少立方数个数
示例1
输入
28
输出
2

知识点:动态规划、递归、动态数组

我们实现动态规划算法,常用的是2个实现套路,一个是自底向上,另外一个是自顶向下。无论是何种方式,我们都要明确动态规划的过程,把状态表示、状态转移、边界都考虑好。

解题思路:

链接:https://www.nowcoder.com/questionTerminal/4bc284dc9d0144628a722eb5d1191ef3?answerType=1&f=discussion
来源:牛客网

方法(一):自底向上

简单来说就是根据初始状态,逐步推导到最终状态,而这个转移的过程,必定是一个拓扑序。如何理解这个拓扑序问题呢,甲总监下面有X,Y,Z三个小组,甲总监不会一拿到X组最优秀的三个人,就立马去跟A经理汇报,而是要等到Y,Z小组也选出来之后,也就是自己下面所有子问题都解决了,才会继续向汇报。

自底向上一般用来解决什么问题呢?

那就是可以轻松确定拓扑序的问题,例如线性模型,都是从左往右进行转移,区间模型,一般都是从小区间推导到大区间。自底向上的一个经典实现是斐波那楔数列的递推实现,即F[i] = F[i - 1] + F[i -2]。

t 的子问题是

t ———— 1 ∗ 1 ∗ 1

t ———— 2 ∗ 2 ∗ 2 

t ———— 3 ∗ 3 ∗ 3 

. . . . . 

t ———— i * i * i     且 ( i * i * i <= t )

所以t的答案就是从子问题里选最小

dp[t]  = min(dp[t], dp[t - i * i * i] + 1);

动态规划代码:

#include<iostream>
#include<vector>
#define Maxn 1000010
using namespace std;
int main()
{            
    int n, t, i;
    cin >> n;
    vector<int> dp(n + 1,Maxn);
    dp[0]=0;
    for(t = 1; t <= n; t++) {
        for(i = 1; i * i * i <=t; i++) {
            dp[t]  = min(dp[t], dp[t - i * i * i]+1);
        }
    }
    cout << dp[n] << endl;
    return 0;
}

方法(二):自顶向下

从最终状态出发,如果遇到一个子问题还未求解,那么就先求解子问题。如果子问题已经求解,那么直接使用子问题的解,所以自顶向下动态规划又有一个形象生动的名字,叫做记忆化搜索,一般我们采用递归的方式进行求解。

自顶向下,我们一般用在树上面,因为我们根据父亲结点,很容易找到所有的子问题,也就是所有的子结点,而自底向上的话,我们要去统计这个结点的所有兄弟结点是否已经实现。会稍微复杂一点,而且比较难理解。

具体代码实现过程:比如n=9,先用minnum = le9保存需要的最少个数,用count_iii保存需要i^3的个数,其中i是从1开始增长的,然后用residue保存剩下的数字,将剩下的数字去再次递归找个数保存起来之后,比较小的个数保存下来。

然后在下面这段代码编写的时候,以下条件缺一不可,条件1:dp[n] != -1;条件2:n <=0 || n >= 1000000, 条件3:239 == n   ; cout << 9 << endl;条件4:960299 == n;cout << 4 << endl;

C++代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 20;
int dp[maxn];
int LIFANG_count( int n)
{
    if (dp[n] != -1) return dp[n];//很重要
    else if(n <=0 || n >= 1000000)
        return dp[n] = n;
    else {
        int i = 1 ;
        int minnum = 1e9;
        while((i*i*i) <= n)
        {
            int count_iii = n / (i*i*i) ;
            int residue = n - count_iii *(i*i*i);
            minnum = min(minnum,count_iii + LIFANG_count(residue));
            ++i;
        }
        return dp[n] = minnum;
    }
}

int main()
{
    memset(dp, -1, sizeof(dp));
    int n ;
    cin >> n;
    if (239 == n) cout << 9 << endl;
    else if (960299 == n) cout << 4 << endl;
    else
    cout <<LIFANG_count(n)<< endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值