动态规划入门,从01背包开始!

动态规划

核心 : 状态的表示和 状态的计算

  • 状态的表示:需要有几维来表示?每一个状态表示什么?

    ​ 状态f(i.j):集合 + 属性

    ​ 集合:所有可选的方案、条件(只从前i个物体中选、总体积小于j)

    ​ 属性:Max、Min、数量

  • 状态的计算:如何一步步把状态算出来?

    ​ 集合划分的原则:不重不漏

    ​ 集合的划分——不含i: $f(i-1,j) $ 、含i: \(f(i-1,j-v_i)+w_i\)

    ​ f(i,j) = max ( $f(i-1,j) $ ,\(f(i-1,j-v_i)+w_i\) )

背包问题

  • 01背包问题:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品最多只能用一次,背包能装得下的情况下,选出物品的总价值最大
  • 完全背包:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品可以用无限次,背包能装得下的情况下,选出物品的总价值最大
  • 多重背包:N个物体,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,每件物品最多有\(S_i\) 个,背包能装得下的情况下,选出物品的总价值最大
  • 分组背包:N组物体,每组物品若干种,每组最多选一个物品,容量是V的背包,每个物品有两个属性:体积为\(V_i\) , 价值为\(W_i\) ,背包能装得下的情况下,选出物品的总价值最大

01背包

二维写法:

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;
int v[N];
int w[N];
int f[N][N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> v[i] >> w[i];
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= m; j++)
        {
            f[i][j] = f[i - 1][j];
            if(j >= v[i])
                f[i][j] = max(f[i][j],f[i - 1][j - v[i]]+ w[i]);
        }
    cout << f[n][m] << endl;
    return 0;
}

优化后的一维写法

由观察可得:f(i,j)之和f(i-1,j)以及f(i-1,j-v[i])有关,可优化为一维

注意:内层循环是倒序遍历!防止一个物体被添加多次!

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;
int v[N];
int w[N];
int f[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> v[i] >> w[i];
    for(int i = 1; i <= n; i++)
        for(int j = m; j >= v[i]; j--)
        {
            f[j] = max(f[j],f[j - v[i]]+ w[i]);
        }
    cout << f[m] << endl;
    return 0;
}

完全背包

  • 01背包问题每个物品状态分两种,即某物品选or不选

  • 完全背包每个每个物品状态分无数种,即某物品选多少个

f[i,j] = Max(f[i-1,j], f[i-1,j-v]+w,f[i-1,j-2v]+2w,f[i-1,j-3v]+3w...)

f[i,j-v] = Max( f[i-1,j-v], f[i-1,j-v]+w , f[i-1,j-v]+2w....)

因此f[i,j]可以简化为max(f[i-1,j],f[i,j-v]+w)

写法与01背包类似,二维数组只需要把f(i-1,j-v[i])改成f(i,j-v[i])

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;
int v[N];
int w[N];
int f[N][N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> v[i] >> w[i];
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= m; j++)
        {
            f[i][j] = f[i - 1][j];
            if(j >= v[i])
                f[i][j] = max(f[i][j],f[i][j - v[i]]+ w[i]);
        }
    cout << f[n][m] << endl;
    return 0;
}

一维数组法只需要把内层 循环从倒序改为正序

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;
int v[N];
int w[N];
int f[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> v[i] >> w[i];
    for(int i = 1; i <= n; i++)
        for(int j = v[i]; j <= m ; j++)
        {
            f[j] = max(f[j],f[j - v[i]]+ w[i]);
        }
    cout << f[m] << endl;
    return 0;
}
posted @ 2022-08-12 21:35  火车驶向云外0218  阅读(58)  评论(1)    收藏  举报