背包问题 个人学习笔记及 代码

0/1 背包问题

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>//个人 一般习惯写全头文件
using namespace std;
#define N 1010
int f[N];
int m,n;//商品数m 以及最大容纳商品体积n
int v[N],jz[N];//体积以及价值
int main()
         {
             int i,j;
           cin>>m>>n;
            // scanf("%d %d",&m,&n);
             for(i=1;i<=m;i++)//初始化
             {
                cin>>v[i]>>jz[i];//输入 体积以及价值

             }

            for(i=1;i<=m;i++)//遍历 商品
                for(j=n;j>=v[i];j--)//f[j] 表示的是 可容纳j 体积时可得到的 最大价值
                {  f[j]=max(f[j], f[j-v[i]]+jz[i]);//f 这里考虑的是 是否可以 容纳第i 个商品 若可以f[j-v[i]]+jz[i]  不可以 f[j] 及价值体积均无变化


                }
  cout<<f[n]<<endl;//最后的f[n]  就表示为 最大 体积为n使得最大价值 ,并不一定 取的商品体积恰好等于 n

            return 0;

         }

 

2 完全背包问题 也就是 每件商品可使用的次数无限制

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define  N 1010
int f[N];//表示最大体积为N是的最大价值能是多少
int v,w;
int m,n;
int main()
{
    int i,j;
    cin>>m>>n;
    for(i=0;i<m;i++)
    {
        cin>>v>>w;//直接输入i件商品的 体积以及价值
        for(j=v;j<=n;j++)//每次循环得到的都是  使用重复次使用前i+1个商品的最大价值
            f[j]=max( f[j], f[j-v]+w);
            //可对比 0/1 背包问题 这里仍然使用更新 f[j] 但是 这里的f[j] 已经是包含(可能 )无限次前i件商品
            //j不断地-v就是使用重复次 i件商品                                  f的数组标表示还可容纳的体积
    }
    cout<<f[n]<<endl;
}

3 完全背包问题

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define  N 1010
int f[N];//表示最大体积为N是的最大价值能是多少
int v,w;
int m,n,s;
int main()
{
    int i,j;
    cin>>m>>n;
    for(i=0;i<m;i++)
    {
        cin>>v>>w>>s;//直接输入i件商品的 体积以及价值
//            //可对比 0/1 背包问题 这里仍然使用更新 f[j] 但是 这里的f[j] 已经是包含(可能 )无限次前i件商品
             for(j=n;j>=0;j--)
                for(int k=1;k<=s&&k*v<=j;k++)
                f[j]=max(f[j],f[j-k*v]+k*w);//与0/1 背包不同的是可以选择 商品个数0--s个所以多了一层循环
            //  f的数组标表示还可容纳的体积
    }
    cout<<f[n]<<endl;
}

                   ----------------------------之前的都是数组大概有 1000个

--------------------------------------------当超过1000 如下  就会出现时间超限,如下题

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,VN,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 NN 行,每行三个整数 vi,wi,sivi,wi,si,用空格隔开,分别表示第 ii 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤10000<N≤1000
0<V≤20000<V≤2000
0<vi,wi,si≤20000<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

--------------------------------------------------

解法:这里另一种写法就是 将  每件商品的 可使用数量用二进制拆分  比如10 可用4位二进制 表示出来,但是10 并非是2^4 (最大表示) 所以每次拆分最后一位应该是 10-1-2-4-8=-4 当减少到 为负数时,那么就应该最后一位为10-1-2-4=3 也就是10可拆分为1 2 4 3 这里使用一个数组来存储拆分项

 之后根据拆分项将商品的每件每件的分离,形成0/1背包问题

,比如10件价值为v的商品 拆分为

1件 1*v

1件2*v

1件4*v

1件3*v 问题就转化为0/1背包问题,

代码如下

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int N,V,v[1001],w[1001],f[2001],s[1001]
    int a[25000],b[25000];  //2的12次方大于2000,也就是说一个数最多可以拆成12个,故数组容量乘12 
    cin>>N>>V;        
    memset(f,0,sizeof(f));
    for(int i=0;i<N;i++)
     cin>>v[i]>>w[i]>>s[i];
    int total=0;  
    for(int i=0;i<N;i++)
    {
        for(int j=1;j<s[i];j<<=1)//二进制拆分 
         {
            a[total]=j*w[i];//存价值 
            b[total++]=j*v[i];//存容量 
            s[i]-=j;
         }
         if(s[i])//当s[i]>0; 
         {
             a[total]=s[i]*w[i];
             b[total++]=s[i]*v[i];
         }
    }
    for(int i=0;i<total;i++)//01背包 
     for(int j=V;j>=b[i];j--)
      f[j]=max(f[j],f[j-b[i]]+a[i]);
    cout<<f[V];
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值