0-1 多重 组合背包的自学之路(不断更新中)

2024-08-31 00:58

本文主要是介绍0-1 多重 组合背包的自学之路(不断更新中),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文定义(代码属于个人理解+手打)参考于:http://www.cnblogs.com/tanky_woo/archive/2010/07/31/1789621.html 

谢谢分享,感谢原作者,个人对其略加内容,还有就是感谢(聪明)kiwi的pdf。晚安~

晚上打了几盘地狱人机,简直被打爆,然后打玩家开具4-17逆风局,我用剑圣3次四杀(两次被抢pentakill)!31-7-4的人头比超神翻盘打爆对面卡特



01背包

01背包(ZeroOnePack): 有N件物品和一个容量为V的背包。(每种物品均只有一件)第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:

f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

把这个过程理解下:在前i件物品放进容量v的背包时,

它有两种情况:

第一种是第i件不放进去,这时所得价值为:f[i-1][v]

第二种是第i件放进去,这时所得价值为:f[i-1][v-c[i]]+w[i]

(第二种是什么意思?就是如果第i件放进去,那么在容量v-c[i]里就要放进前i-1件物品)

最后比较第一种与第二种所得价值的大小,哪种相对大,f[i][v]的值就是哪种。

(这是基础,要理解!)

这里是用二位数组存储的,可以把空间优化,用一位数组存储。

用f[0..v]表示,f[v]表示把前i件物品放入容量为v的背包里得到的价值。把i从1~n(n件)循环后,最后f[v]表示所求最大值。

*这里f[v]就相当于二位数组的f[i][v]。那么,如何得到f[i-1][v]和f[i-1][v-c[i]]+w[i]?(重点!思考)
首先要知道,我们是通过i从1到n的循环来依次表示前i件物品存入的状态。即:for i=1..N
现在思考如何能在是f[v]表示当前状态是容量为v的背包所得价值,而又使f[v]和f[v-c[i]]+w[i]标签前一状态的价值?

逆序!

这就是关键!

 

1
2
3
for  i=1..N
    for  v=V..0
         f[v]=max{f[v],f[v-c[i]]+w[i]};

 

 

 

分析上面的代码:当内循环是逆序时,就可以保证后一个f[v]和f[v-c[i]]+w[i]是前一状态的!
这里给大家一组测试数据:

测试数据:
10,3
3,4
4,5
5,6

 

 

题目: http://acm.hdu.edu.cn/showproblem.php?pid=2602
代码:

#include<cstdio>
#include<cstring>
int f[1024];
int w[1024];
int c[1024];
int main()
{int t;scanf("%d",&t);while(t--){int n,m;scanf("%d %d",&n,&m);for(int i=0;i<n;i++) scanf("%d",&c[i]);for(int i=0;i<n;i++) scanf("%d",&w[i]);memset(f,0,sizeof(f));for(int i=0;i<n;i++){for(int j=m;j>=w[i];j--){if(f[j-w[i]]+c[i]>f[j])//定义为装入c[i]物品的第[j-w[i]]位置,依次刷新f[j]=f[j-w[i]]+c[i];//恰巧此时f[j-w[i]]为上一状态,所以可以拿来使用,}                           //所以说就是在每一个质量位上一直在累加}printf("%d\n",f[m]);}return  0;
}


02完全背包:

完全背包(CompletePack): 有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

完全背包按其思路仍然可以用一个二维数组来写出:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

同样可以转换成一维数组来表示:

伪代码如下:

1
2
3
for  i=1..N
     for  v=0..V
         f[v]=max{f[v],f[v-c[i]]+w[i]}

顺序!

想必大家看出了和01背包的区别,这里的内循环是顺序的,而01背包是逆序的。
现在关键的是考虑:为何完全背包可以这么写?

在次我们先来回忆下,01背包逆序的原因?是为了是max中的两项是前一状态值,这就对了。
那么这里,我们顺序写,这里的max中的两项当然就是当前状态的值了,为何?
因为每种背包都是无限的。当我们把i从1到N循环时,f[v]表示容量为v在前i种背包时所得的价值,这里我们要添加的不是前一个背包,而是当前背包。所以我们要考虑的当然是当前状态。

#include<cstdio>
#include<cstring>
int f[1024];
int w[1024];
int c[1024];
int main()
{int t;scanf("%d",&t);while(t--){int n,m;scanf("%d %d",&n,&m);for(int i=0;i<n;i++) scanf("%d",&c[i]);for(int i=0;i<n;i++) scanf("%d",&w[i]);memset(f,0,sizeof(f));//		for(int i=0;i<n;i++)
//		{
//			for(int j=m;j>=w[i];j--)
//			{
//				if(f[j-w[i]]+c[i]>f[j])
//				f[j]=f[j-w[i]]+c[i];
//			}
//		}for(int i=0;i<n;i++){for(int j=w[i];j<=m;j++){if(f[j-w[i]]+c[i]>f[j])//作为先序首先无限对高端值钱的添加f[j]=f[j-w[i]]+c[i];//就是对0-1背包的改版}}printf("%d\n",f[m]);}}


03多重背包  
         多重背包转换成 01 背包问题就是多了个初始化,把它的件数C 用二进制分解成若干个件数的集合,这里面数字可以组合成任意小于等于C的件数,而且不会重复,之所以叫二进制分解,是因为这样分解可以用数字的二进制形式来解释  
       比如:7的二进制 7 = 111 它可以分解成 001 010 100 这三个数可以组合成任意小于等于7 的数,而且每种组合都会得到不同的数  
       15 = 1111 可分解成 0001  0010  0100  1000 四个数字  
        如果13 = 1101 则分解为 0001 0010 0100 0110 前三个数字可以组合成  7以内任意一个数,即1、2、4可以组合为1——7内所有的数,加上 0110 = 6 可以组合成任意一个大于6 小于等于13的数,比如12,可以让前面贡献6且后面也贡献6就行了。虽然有重复但总是能把 13 以内所有的数都考虑到了,基于这种思想去把多件物品转换为,多种一件物品,就可用01 背包求解了。  

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int dp[100005];
int w[1000];
int c[1000]; 
int main(){int n,m,a,b,cc,cnt;while(cin >> n >> m) //{cnt=1;memset(dp,0,sizeof(dp)); memset(c,0,sizeof(c));memset(w,0,sizeof(w));for(int i=1;i<=n;i++){cin >> a >> b >> cc;       //a个东西质量为b,jiazhiwei cc for(int j=1;j<=a;j*=2){w[cnt]=j*b;c[cnt++]=cc*j;a-=j;}if(a>0){w[cnt]=a*b;c[cnt++]=cc*a;}}dp[0]=0; for(int i=1;i<=cnt;i++)for(int j=m;j>=w[i];j--)dp[j]=max(dp[j],dp[j-w[i]]+c[i]);cout << dp[m] <<endl;}return 0;
}



这篇关于0-1 多重 组合背包的自学之路(不断更新中)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122435

相关文章

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

csu(背包的变形题)

题目链接 这是一道背包的变形题目。好题呀 题意:给n个怪物,m个人,每个人的魔法消耗和魔法伤害不同,求打死所有怪物所需的魔法 #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>//#include<u>#include<map

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4869(逆元+求组合数)

//输入n,m,n表示翻牌的次数,m表示牌的数目,求经过n次操作后共有几种状态#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#includ

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

uva 10130 简单背包

题意: 背包和 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

多重背包转换成0-1背包

http://acm.hdu.edu.cn/showproblem.php?pid=2191 多重背包特点: 一种物品有C个(既不是固定的1个,也不是无数个) 优化的方法: 运用神奇的二进制,进行物品拆分,转化成01背包 物品拆分,把13个相同的物品分成4组(1,2,4,6) 用这4组可以组成任意一个1~13之间的数! 原理:一个数总可以用2^