首页 > 其他分享 >动态规划笔记(二):背包问题(未整理完)

动态规划笔记(二):背包问题(未整理完)

时间:2023-01-14 12:34:22浏览次数:43  
标签:未整理 int max 复杂度 cin 笔记 背包 include dp

背包问题

0/1背包问题(HDU-2602)

状态转移方程:\(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i])\)

  • 递推
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[N][N];

void solve()
{
    int n, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];
    for (int i = 1; i <= n; i ++ ) cin >> v[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= V; j ++ )	// 这题j必须从0开始不然WA,可能是因为存在v[i] = 0
        {
            if (j >= v[i]) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
            else dp[i][j] = dp[i - 1][j];
        }
    cout << dp[n][V] << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while (t -- )
    {
        solve();
    }

    return 0;
}

时间复杂度为\(O(nV)\)

  • 记忆化
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[N][N];

int solve(int i, int j)
{
    if (dp[i][j]) return dp[i][j];
    if (i == 0) return 0;
    if (j >= v[i]) return dp[i][j] = max(solve(i - 1, j), solve(i - 1, j - v[i]) + w[i]);
    return dp[i][j] = solve(i - 1, j);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while (t -- )
    {
        memset(dp, 0, sizeof dp);
        int n, V;
        cin >> n >> V;
        for (int i = 1; i <= n; i ++ ) cin >> w[i];
        for (int i = 1; i <= n; i ++ ) cin >> v[i];
        cout << solve(n, V) << '\n';
    }

    return 0;
}

时间复杂度为\(O(nV)\)

  • 滚动数组优化

由状态转移方程可得,\(dp[i][]\)只与\(dp[i - 1][]\)有关,因此可用滚动数组优化空间

  1. 交替滚动
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[2][N];

void solve()
{
    memset(dp, 0, sizeof dp);
    int n, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];
    for (int i = 1; i <= n; i ++ ) cin >> v[i];
    int now = 0, prev = 1;
    for (int i = 1; i <= n; i ++ )
    {
        swap(now, prev);
        for (int j = 0; j <= V; j ++ )
            if (j >= v[i]) dp[now][j] = max(dp[prev][j], dp[prev][j - v[i]] + w[i]);
            else dp[now][j] = dp[prev][j];
    }
    cout << dp[now][V] << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t;
    cin >> t;
    while (t -- )
    {
        solve();
    }

    return 0;
}

时间复杂度不变,但空间复杂度由原先的\(O(nV)\)变为\(O(V)\)

  1. 自我滚动

    注意:\(j\)必须从大到小遍历

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    const int N = 1010;
    int w[N], v[N], dp[N];
    
    void solve()
    {
        memset(dp, 0, sizeof dp);
        int n, V;
        cin >> n >> V;
        for (int i = 1; i <= n; i ++ ) cin >> w[i];
        for (int i = 1; i <= n; i ++ ) cin >> v[i];
        // for (int i = 1; i <= n; i ++ )
        //     for (int j = V; j >= 0; j -- )
        //         if (j >= v[i]) dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
        // 下方与上方被注释的代码等价
        for (int i = 1; i <= n; i ++ )
            for (int j = V; j >= v[i]; j -- )
                dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
        cout << dp[V] << '\n';
    }
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
    
        int t;
        cin >> t;
        while (t -- )
        {
            solve();
        }
    
        return 0;
    }
    

同样时间复杂度不变,空间复杂度由原先的\(O(nV)\)变为\(O(V)\)

完全背包问题(Acwing)

状态转移方程:\(dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i], dp[i - 1][j - 2 * v[i]] + 2 * w[i], ..., dp[i - 1][j - k * v[i]] + k * w[i])\)

  • 朴素做法(TLE)
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= V; j ++ )
            for (int k = 0; k * v[i] <= j;  k ++ )
                dp[i][j] = max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
    cout << dp[n][V] << '\n';

    return 0;
}

这个时间复杂度不会算,但最坏情况下时间复杂度可以估成\(O(nV^2)\)

  • 时间优化

由状态转移方程\(dp[i][j - v[i]] = max(dp[i - 1][j - 2 * v[i]] + w[i], ...,dp[i][j - k * v[i]] + (k - 1) * w[i])\)可得:\(dp[i][j] = max(dp[i - 1][j], dp[i][j - v[i]] + w[i])\)

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= V; j ++ )  // j必须从小到大遍历
            if (j >= v[i]) dp[i][j] = max(dp[i - 1][j], dp[i][j - v[i]] + w[i]);
            else dp[i][j] = dp[i - 1][j];
    cout << dp[n][V] << '\n';

    return 0;
}

时间复杂度为\(O(nV)\)

  • 交替滚动
#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int w[N], v[N], dp[2][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, V;
    cin >> n >> V;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    int now = 0, prev = 1;
    for (int i = 1; i <= n; i ++ )
    {
        swap(now, prev);
        for (int j = 1; j <= V; j ++ )
            if (j >= v[i]) dp[now][j] = max(dp[prev][j], dp[now][j - v[i]] + w[i]);
            else dp[now][j] = dp[prev][j];
    }
    cout << dp[now][V] << '\n';

    return 0;
}

时间复杂度与时间优化后一样,空间复杂度由时间优化后的\(O(nV)\)变为\(O(V)\)

标签:未整理,int,max,复杂度,cin,笔记,背包,include,dp
From: https://www.cnblogs.com/Panmaru/p/17051576.html

相关文章

  • 动态规划笔记(一):初识DP
    动态规划(DP)DP问题特征特征:重叠子问题、最优子结构、无后效性重叠子问题:计算大问题需要重复计算小问题,DP可以避免重复计算,代价是消耗更多的空间最优子结构:大问题的最优......
  • 随机过程的思维导图和笔记
    简单梳理了一下随机过程前四章的内容,这四章联系还是很紧密的。以第一章为基础,延伸出第二章的多种poisson过程,包括复合、稀释、叠加以及条件分布的poisson过程。第三章应用......
  • Java学习笔记10
    1.抽象类1.1概述​ 没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。抽象方法:没有方法体的方法。抽象类:包含抽象方法的类。1.2abstract......
  • 圆方树学习笔记
    部分内容参照了OI-wiki定义对于这样的一个无向图,左侧的\({1,2,3}\)和右侧的\({3,4,5}\)分别构成一个点双联通分量。中间的\(3\)号节点就是一个割点。不难发现,点双......
  • 【读书笔记】JS函数式编程指南
    第一章海鸥群可以合并和繁育conjoinbreedvarresult=flock_a.conjoin(flock_c).breed(flock_b).conjoin(flock_a.breed(flock_b)).seagulls;但是由于有内部状态,内......
  • 数论笔记-整除
    目录整除整除的定义与基本性质素数素数的定义与基本性质素数判定试除法\(kn+i\)法预处理法Miller-Rabin素性测试素数筛法埃氏筛欧拉筛(线性筛)反素数反素数的定义与基本性质......
  • JavaScript学习笔记—对象
    对象中可以存储多个各种类型的数据,对象中存储的数据成为属性添加属性或修改属性值:对象.属性名=属性值读取属性:对象.属性名,如果读取对象中没有的属性返回undefined删......
  • aBiu的笔记汇总
    上车Java8新特性拉取测试代码JUC来源b站狂神说测试代码SpringBootspirng源码大致流程SpringSecurity来源Bilibili黑马程序员视频教程SpringSecurity认证授......
  • 算法学习笔记(2): 逆元及其应用
    #逆元[TOC]##定义逆元素,是指一个可以取消另一给定元素运算的元素具体来说,对于实际的一些应用,如:当我们想要求`(11/3)%10`时 明显可以看出,是没有办法直接算的,这时就......
  • 安卓笔记 0 加载模板和设置事件的DEMO
    在onCreate的方法中加载模板2种主要方式:1:setContentView(R.layout.activity_main);2:LinearLayoutmainLinearLayout=(LinearLayout)getLayoutInflate......