ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

背包基础

2022-01-25 12:32:36  阅读:123  来源: 互联网

标签:背包 容量 int 基础 cin 物品 include


四种基础背包问题


总结:背包问题是我接触的第一类DP问题,也是我之前很长时间都感到恐慌的一类题,在深入学习DP前,不得不牢固掌握这些基础问题,由此总结



>01背包

问题:有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。



二维版本

(1)状态表示:\(f[i][j]:在前i个物品中选择,且背包容量小于等于j的选法的集合\) \(属性:MAX\)

(2)状态计算:\(根据选不选第i个物品来划分集合\)

算法分析
\(1.背包容量不够的时候(j < v[i]):没得选,因此前i个物品最优解即为前i-1个物品最优解,即 f[i][j] = f[i - 1][j]\)
\(2.背包容量够的时候,可以选,因此有两种情况:选或不选,选:f[i - 1][j - v[i]] + w[i],不选:f[i - 1][j],结果取其最大值\)

代码

#include<iostream>
using namespace std;

const int N = 1010;

int f[N][N];
int v[N],w[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 = 1;j <= m;j++)
     {
         f[i][j] = f[i - 1][j];
         if(j >= v[i]) f[i][j] = max(f[i - 1][j],f[i - 1][j - v[i]] + w[i]);
     }
     
     cout<<f[n][m]<<endl;
     return 0;
}

一维版本:二维的等价变形

(1)状态表示:\(f[j]:N件物品且背包容量j下的最优解\)

(2)背包容量j必须从m开始枚举

(3)为什么要逆序枚举? 二维情况下,f[i][j]是由i-1轮的状态更新过来的。优化到一维后,如果还是正序,则有f[较小体积]更新到f[较大体积],则有可能本应该用i - 1轮的状态,最后却用的是第i轮的。


\(eg:因为由于是滚动数组,我们必须是用上一层i - 1的状态,假设f[8] 是由 f[5] 更新过来,如果顺序枚举,则f[5]可能在f[8]之前被更新过,这样就是用到f[i][5],显然不对。逆序枚举的话,f[8]同样由f[5]更新过来,但是f[5]还没进行第i轮的计算,因此计算的结果仍然是f[i-1][5],可以说如果顺序枚举,前面的数会被污染。\)


代码

#include<iostream>
using namespace std;

const int N = 1010;

int f[N];
int v[N],w[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;
}

输入优化

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 1005;
int f[MAXN];  // 

int main() 
{
    int n, m;   
    cin >> n >> m;

    for(int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;      // 边输入边处理
        for(int j = m; j >= v; j--)
            f[j] = max(f[j], f[j - v] + w);
    }

    cout << f[m] << endl;

    return 0;
}


>完全背包

问题:有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且可以被任意次,要求在有限的背包容量下,装入的物品总价值最大。



二维版本

(1)状态表示:\(f[i][j]:在前i个物品中选择,且背包容量小于等于j的选法的集合\) \(属性:MAX\)

(2)状态计算:\(根据第i个物品选几个来划分集合\)

算法分析
\(选0个到选到放不下背包,即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]的优化, f[i][j - v] = max(f[i-1][j-v]+f[i-1][j-2v]+w,f[i-1][j-3v]+2w,.......\)
我们发现,f[i][j]的右半部分在j-v的状态下已经算过了,因此可以得递推式:f[i][j] = max(f[i-1][j],f[i][j-v]+w)


代码

#include<iostream>
using namespace std;

const int N = 1010;

int f[N][N];
int v[N],w[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 = 1;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;
}

一维版本:二维的等价变形

(1)状态表示:\(f[j]:N件物品且背包容量j下的最优解\)

(2)背包容量j必须从0开始枚举

(3)顺序枚举 优化到一维后,顺序枚举,f[i][j]可由f[i-1][j]滚动过来,f[i][j-v[i]]+w[i]也是从根据当前i层状态更新,不会像01背包那样被污染,因此顺序枚举。


代码

#include<iostream>
using namespace std;

const int N = 1010;

int f[N];
int v[N],w[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 = 1;j <= m;j++)
     {
         if(j >= v[i])
          f[j] = max(f[j],f[j-v[i]]+w[i]);
     }
     
    cout<<f[m]<<endl;
    return 0;
}


>多重背包

问题:有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且可以被有限次,要求在有限的背包容量下,装入的物品总价值最大。



朴素

(1)状态表示:\(f[i][j]:在前i个物品中选择,且背包容量小于等于j的选法的集合\) \(属性:MAX\)

(2)状态计算:\(根据第i个物品选几个来划分集合,小于等于s[i]个\)

为什么多重背包不能像完全背包那样递推式优化,原因在于:完全背包受体积约束,多重背包受体积和物品个数约束

代码

#include<iostream>
using namespace std;
const int N = 110;
int v[N],w[N],s[N];
int f[N][N];
int n,m;

int main()
{
    cin>>n>>m;
    for(int i = 1;i <= n;i++)
     cin>>v[i]>>w[i]>>s[i];

    for(int i = 1;i <= n;i++)
     for(int j = 0;j <= m;j++)
      for(int k = 0;k <= s[i] &&k * v[i] <= j;k++)
       f[i][j] = max(f[i][j],f[i - 1][j - k * v[i]] + k * w[i]);


    cout<<f[n][m]<<endl;
    return 0;
}


>分组背包

问题:有 N 物品和一个容量为 V 的背包,每组物品有若干个,同一组内的物品最多只能选一个,要求在有限的背包容量下,装入的物品总价值最大。



算法分析

(1)状态表示:\(f[i][j]:在前i个物品中选择,且背包容量小于等于j的选法的集合\) \(属性:MAX\)

(2)状态计算:\(根据第i组物品选第几个来划分集合\)

算法分析
\(每组选一个或者不选,将问题转化成01背包\)


代码

//二维
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110;

int n,m;
int v[N][N],w[N][N],s[N];
int f[N][N];

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

//一维优化空间后
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 110;

int n,m;
int v[N][N],w[N][N],s[N];
int f[N];

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

标签:背包,容量,int,基础,cin,物品,include
来源: https://www.cnblogs.com/wei-ak1/p/15835583.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有