LeetCode279. 完全平方数 DP完全背包

  • https://leetcode.cn/problems/perfect-squares/

题目描述

  • 给你一个整数 n ,返回 和为 n 的完全平方数的最少数量。
    完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:

输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4
示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9
class Solution {
public:
    int numSquares(int n) {

    }
};

完全背包

  • 恰好装满之类的01背包问题,竖向i是物品,横向j是容量。 i为前i件物品,容量j可以为0,算法主题为遍历i,j 。
    由于无后效性,只知道nums[i],需要判断j-nums[i]能做什么。
    j-nums[i]为装了当前物品剩余的空间。dp[ i-1][j-nums[i]]为这点空间能做的事情。如果最大化利润则为以下公式:
    d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w i ] + v i ) dp[i][j]=max(dp[i−1][j],dp[i][j−w _i ]+v_i ) dp[i][j]=max(dp[i1][j],dp[i][jwi]+vi)

  • 完全背包每次还是考虑当前项,只不过可以拿多个,那我们可以加个循环k
    d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w i ∗ k ] + v i ) dp[i][j]=max(dp[i−1][j],dp[i-1][j−w _i\color{red}*k\color{black} ]+v_i ) dp[i][j]=max(dp[i1][j],dp[i1][jwik]+vi)

  • 实际上j是从小到大遍历的,所以可以用
    d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w i ] + v i ) dp[i][j]=max(dp[i−1][j],dp[\color{red}i\color{black}][j−w _i ]+v_i ) dp[i][j]=max(dp[i1][j],dp[i][jwi]+vi)
    省略掉一个循环

  • 0-1背包如果压缩维度dp[i][j-nums[i]],但是j需要倒序遍历。

完全背包

        完全背包是一类经典的背包问题,与0/1背包问题和多重背包问题不同。其中,每个物品可以被无限次地选择放入背包。也就是说,每个物品的数量是无限的。

        假设有一个容量为 V V V的背包,现在有 n n n个物品,每个物品的体积为 w i w_i wi,价值为 v i v_i vi,同时每个物品的数量是无限的。问能够放入背包的最大价值是多少?

        完全背包问题可以通过动态规划求解,时间复杂度为 O ( n V ) O(nV) O(nV)。可以用一个二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j]来表示前 i i i个物品放入容量为 j j j的背包中获得的最大价值。其中,状态转移方程为 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w i ] + v i ) dp[i][j]=max(dp[i-1][j],dp[i][j-w_i]+v_i) dp[i][j]=max(dp[i1][j],dp[i][jwi]+vi),表示当前物品不放入背包或者放入背包均可。

  • (二维的转移方程还是比较好记的,只需要将 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w i ] + v i ) dp[i][j]=max(dp[i-1][j],dp[\color{red}i-1\color{black}][j-w_i]+v_i) dp[i][j]=max(dp[i1][j],dp[i1][jwi]+vi)写成i就行)

13 = 9 = 3 \sqrt {13} = \sqrt{9} = 3 13 =9 =3

0 1 2 3 4 5 6 7 8 9 10 11 12 13
1
2
3

" 能够放入背包的最大价值 " ⇒ " 能够放入背包的最小价值 " ( 每个数的价值为 1 ) "能够放入背包的最大价值" \Rightarrow "能够放入背包的最小价值"\tiny(每个数的价值为1) "能够放入背包的最大价值""能够放入背包的最小价值"(每个数的价值为1)
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w i ] + v i ) dp[i][j]=max(dp[i-1][j],dp[i][j-w_i]+v_i) dp[i][j]=max(dp[i1][j],dp[i][jwi]+vi)

LeetCode279. 完全平方数 DP完全背包_第1张图片
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w i ] + 1 ) dp[i][j]=min(dp[i-1][j],dp[i][j-w_i]+1) dp[i][j]=min(dp[i1][j],dp[i][jwi]+1)

class Solution {
public:
    // 求平方后小于 n 的最大完全平方数
    int min_sq(int n) {
        int ans = 1;
        for ( int i=1;i<n;i++ ) {
            if ( i*i>n )
                break;
            ans = i;
        }
        return ans;
    }

    int numSquares(int n) {

        // // 记忆化搜索
        // int m = min_sq(n), cache[m+1][n+1];
        // memset(cache, -1, sizeof(cache));
        // function<int(int,int)> dfs = [&](int i, int c) -> int {
        //     if ( i<0 )
        //         return c==0 ? 0 : INT_MAX/2;
        //     if ( cache[i][c]!=-1 )
        //         return cache[i][c];
        //     if ( c<(i+1)*(i+1) )
        //         return cache[i][c] = dfs(i-1, c);
        //     return cache[i][c] = min(dfs(i-1, c), dfs(i, c-(i+1)*(i+1))+1);
        // };

        // return dfs(m-1, n);

        // // 1:1 递推
        // int m = min_sq(n), f[m+1][n+1];
        // memset(f, 0x3f, sizeof(f));
        // f[0][0] = 0;
        // for ( int i=0;i<m;++i ) {
        //     for ( int c=0;c<n+1;++c ) {
        //         if ( c<(i+1)*(i+1) )
        //             f[i+1][c] = f[i][c];
        //         else
        //             f[i+1][c] = min(f[i][c], f[i+1][c-(i+1)*(i+1)]+1);
        //     }
        // }
        // return f[m][n];

        // // 滚动双数组空间优化
        // int m = min_sq(n), f[2][n+1];
        // memset(f, 0x3f, sizeof(f));
        // f[0][0] = 0;
        // for ( int i=0;i<m;++i ) {
        //     for ( int c=0;c<n+1;++c ) {
        //         if ( c<(i+1)*(i+1) )
        //             f[(i+1)%2][c] = f[i%2][c];
        //         else
        //             f[(i+1)%2][c] = min(f[i%2][c], f[(i+1)%2][c-(i+1)*(i+1)]+1);
        //     }
        // }
        // return f[m%2][n];

        // 单数组空间优化
        int m = min_sq(n), f[n+1];
        memset(f, 0x3f, sizeof(f));
        f[0] = 0;
        for ( int i=0;i<m;++i ) {
            for ( int c=0;c<n+1;++c ) {
                if ( c>=(i+1)*(i+1) )
                    f[c] = min(f[c], f[c-(i+1)*(i+1)]+1);
            }
        }
        return f[n];
    }
};

// 作者:thirikid
// 链接:https://leetcode.cn/problems/perfect-squares/solution/ling-shen-wan-quan-bei-bao-hui-su-ji-yi-d48mb/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

一维

这个问题可以通过动态规划求解。我们可以用一个一维数组 d p dp dp 来表示和为 i i i 的完全平方数的最少数量,状态转移方程为:

d p [ i ] = min ⁡ j = 1 i { d p [ i − j 2 ] + 1 } dp[i] = \min_{j=1}^{\sqrt{i}} \{dp[i-j^2] + 1\} dp[i]=j=1mini {dp[ij2]+1}

其中, j j j 表示一个完全平方数, i − j 2 i-j^2 ij2 表示剩余的部分。也就是说,要计算和为 i i i 的完全平方数的最少数量,我们需要枚举最后一个完全平方数 j j j,并计算剩余部分 i − j 2 i-j^2 ij2 的最少数量,最后加上 1 即可。

初始状态为 d p [ 0 ] = 0 dp[0] = 0 dp[0]=0,因为和为 0 0 0 的完全平方数数量为 0 0 0

最终的答案为 d p [ n ] dp[n] dp[n]。以下是Python代码实现:

def numSquares(n: int) -> int:
    # 初始化状态
    dp = [float('inf')] * (n + 1)
    dp[0] = 0

    # 状态转移
    for i in range(1, n+1):
        for j in range(1, int(i ** 0.5) + 1):
            dp[i] = min(dp[i], dp[i - j*j] + 1)

    return dp[n]

你可能感兴趣的:(笔记,算法,leetcode,职场和发展)