0-1背包问题的解决学习笔记(多方法)

任务描述

本关任务:考虑如下定义的背包问题K(U, C):设U={u(1), u(2),…, u(n)}是一个准备放入容量为C的背包中的n个物品的集合,第i个物品u(i)具有体积s(i)和价值v(i),要求从这n个物品中挑选出一部分装入背包,在不超过背包容量的前提下使背包中物品的价值最大。这里C, s(i)和v(i)都为正整数,且所有的s(i)都不大于C。

编程要求

程序将先输入背包的容量C(1<=C<=1000)和物品的个数n,然后依次输入n(1<=n<=150)个物品的体积s(i)和价值v(i)(i=1, 2, 3, … , n),输出能够装入背包中物品总价值的最大值。

算法

1.动态规矩

原理

动态规划求解:

设函数v(i,j)表示从前i项物品中取出的装入容量为j的背包的物品的最大价值,i和j都为整数,且0<=i<=n, 0<=j<=C:
显然v(n,C)就是我们需要得到的结果。对于上述递推式,
v[0,j]=0是因为没有物品可供挑选放入背包;
v[i,0]=0是因为背包容量为0,自然也不能装入任何物品;
当s(i)>j时,表示该物品超过背包容量,不可能出现在最后的解中,因此v[i,j]=v[i-1,j];

对于其它情况,v[i,j]的值为v[i-1,j]和v[i-1,j-s_i]+v(i)中的最大者,这对应了最优解是否需要包含第i个物品,也反映出v[i,j]的值依赖于v[i-1,j]和v[i-1,j-s(i)]这两个子问题的解。

代码实现

#include <iostream>
using namespace std;
int n, C,s[2000], v[2000];
int value(int i,int j)
{if (i==-1) return 0;
    if (s[i]>j) return value(i-1,j);
 
else   return max(value(i-1,j),(value(i-1,j-s[i])+v[i]));
}



int main() 
{
  
  cin >> C >> n;
  
  for (int i = 0; i < n; i++)
    cin >> s[i] >> v[i];
  
  cout << value(n, C) << endl;
  return 0;
}

虽然但是,如果你在上海西南某高校就读,那你的在线编译器会谴责你超时。

我也不知道为什么,但是事实就是这样,虽然在自己的编译器里也能跑,希望有人能评论区里指出原因,当然你不用函数实现,用for循环似乎也可以完成且不超时。

2.枚举

原理

不可能一一枚举,选项过多,可能也会导致超时,所以我们要尽最大可能减少不可能的情况被选中。
主要思想:全局最优解一定对应有局部最优解。有一点图论的感觉。
不是贪心算法,局部最优解不是指我先挑最贵的。而是说,如果我有一个价值6重量2的物品A,和一个价值2重量2的物品B,则我不可能只拿B。要么只拿A,要么同时拿AB。
实现过程中我也相当于画了一张大图,做了一个二维数组map,i列表示我准备选第i个物品时的背包内情况,j行表示此时我背包被占的内存数,map[j][i]表示此时的已有价值。
值得注意的是,某一个i时刻背包中有多个j行是不为零的,即有多种情况存在,但是同时,每一个map[j][i]是唯一的,对应了前i-1次选择后,内存占用j时的最优解。
每一次迭代即为我在各种内存情况下选择是否要放入第i个物品,并取最优,同样为v[i-1,j]和v[i-1,j-s_i]+v(i)中的最大者。所以算法本质一样,具体看代码实现。

代码实现

//有一点点类似图论的思想?

#include <iostream>
using namespace std;

int main() 
{
  int n, C,s[2000], v[2000],m=0;
  cin >> C >> n;
  int V;
  int map[1001][151]={0};/*这就是一张大图,每一行对应着书包占用相应内存的时候的状态,
  即map[5][1]对应了选择第二个物体时(还未选,书包占用5个内存情况下的包内价值*/
  for (int i = 0; i < n; i++)
    cin >> s[i] >> v[i];
  for (int i = 0; i < n; i++)//一个个选择物体,从零开始
  {
    if(map[s[i]][i]<v[i]) map[s[i]][i+1]=v[i];//必存在什么之前都没选过,只选第i个的情况
    //整体最优解必有局部最优解(图论)
    for (int j = C; j>0 ; j--)
    {
      if (map[j][i]>0)
      {
        if(j+s[i]<=C&&map[j+s[i]][i]<=map[j][i]+v[i])//包里有东西才能放下一个(占更高的内存
        {map[j+s[i]][i+1]=map[j][i]+v[i];//整体最优解必有局部最优解(图论)
      }
       map[j][i+1]=max(map[j][i+1],map[j][i]);//必存在不继续放东西的情况
      }
	  
    }
    for (int i = 0; i <=150; i++){
	for (int j = 0; j<=1000 ; j++){
        m=max(map[j][i],m);//找最大
}}  
    
  }
  
  cout <<  m<< endl;
  return 0;
}

3.遗传算法

原理

纯纯花活了,用01二进制来表示每个物品是否被选入,然后用轮盘算法挑选形状好的基因,赋予更大的被选择概率,但又不绝对被多选择(跳出局部最优解)。最后反复迭代变异,得到较优解。但是启发式算法都有不确定性,考试慎用。

代码实现

写了一个matlab的,cpp还没写完,大佬评论区帮我补吧。

总结

纯纯学习交流,很有可能出现错误,欢迎批评指正,谢谢!
package

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值