概念:
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。
以上来源于百度百科
01背包 是一个十分经典的模型,这个模型能够套在许多问题上。对于这个问题的求解,dp可以说是最后的方法了。
其状态转移方程为:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
当然可以将其优化为一维数组,这些都是在01背包dp上讲的很清楚的东西了,如果不清楚那么模拟一遍不妨是一种很好的办法。
下面我们来看看一些题目:
1.装箱问题
有一个箱子容量为V(正整数,0≤V≤20000),同时有n个物品(0小于n≤30),每个物品有一个体积(正整数)。要求从n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入v,n,在输入n个物品。
输出箱子的剩余空间为最小。
思路:
转换为01背包 可以看做v = w
最后就是V-dp[V]
2 Jin Ge Jin Qu hao UVA12536
题目太长就不贴出来了
简单来讲就是要满足最多歌曲的情况下使得时间最长
其实单独看每一个问题都很好将其转化为01背包 当放在一起的时候其实我们就用两个dp来维护就行了 优先满足数目 在其最优的情况下再去满足时间最优
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100005;
int dp1[maxn] ,dp2[maxn] ,a[105];
int n,t;
int T;
int main(){
scanf("%d",&T);
for(int cas=1 ; cas<=T ;cas++){
scanf("%d%d",&n,&t);
t--;
for(int i=0 ;iscanf("%d",&a[i]);
memset(dp1 ,0 ,sizeof(dp1));
memset(dp2 ,0 ,sizeof(dp2));
for(int i=0 ;ifor(int j=t; j>=a[i] ;j--){
if(dp1[j] < dp1[j-a[i]]+1 ){
dp1[j] = dp1[j-a[i]]+1;
dp2[j] = dp2[j-a[i]]+a[i];
}
else if(dp1[j] == dp1[j-a[i]]+1)
dp2[j] = max(dp2[j] , dp2[j-a[i]]+a[i]);
}
printf("Case %d: %d %d\n",cas,dp1[t]+1,dp2[t]+678);
}
return 0;
}
**3**Buy the souvenirs HDU2126
当寒假来临时,很多人都会去旅行。一般来说,有很多纪念品出售,有时候旅行者会很高兴地买一些。他们不仅可以把纪念品送给朋友和家人作为礼物,还可以回味这些纪念品。总而言之,纪念品的价格并不十分昂贵,也令人喜爱和有趣。但是人们的预算是有限的,他们不能买太多。他们在欣赏了所有的纪念品之后,决定购买某些纪念品,购买时不能购买超过 1 件的同品种纪念品 (即对于同一个品种,要么购买 1 件,要么不购买)。
例如:
产品 A,价格 1 元
产品 B,价格 2 元
产品 C,价格 3 元
产品 D,价格 4 元
如果你只有 7 元钱,则最多可以选择 3 种纪念品的组合,这 3 种纪念品的组合有 ABC (6), ABD (7) 共 2 种方案。如果你有 8 元钱的话,选择最多种类的纪念品有 ABC (6), ABD (7), ACD (8) 共 3 种方案。如果你有 10 元人民币,选择品种最多的纪念品只有 ABCD (10) 这 1 种方案。
输入
输入首先包含一个整数 T,表示测试数据的组数。
对于每组测试数据,在第一行中有两个整数 n, m,其中 n 是纪念品的数量,m 是拥有的预算资金。第二行包含 n 个整数,每个整数描述一种纪念品的价格。
所有的输入数据和输出结果都在 32 位整数的范围内,0 ≤ m ≤ 500 ,0 < n ≤ 30, t ≤ 500,价格都是正整数。两组测试数据之间有一个空行。
输出
如果你可以买一些纪念品,则打印如下格式的结果:“You have S selection(s) to buy with K kind(s) of souvenirs.” (不含引号),其中 K 代表你可以买到的品种最多的纪念品, S 表示可以 K 种纪念品对应的的组合数量。
如果你带的钱不够 (无法购买任何纪念品),则打印结果“Sorry, you can’t buy anything.” (不含引号)。
思路:
与上题类似,只是这题需要维护的是能购买的种类和数量,所以有一个转移的方程不一样,但也很好理解。在此也就不多说了,注意初始条件。
#include
#include
#include
#include
#include
using namespace std;
#define MS(a,x) memset(a,x,sizeof(a))
#define maxn 505
int t;
int a[35];
int m,n;
int f[maxn][2]; //花掉k 0种类 1 selction
void printw(){
printf("Sorry, you can't buy anything.\n");
}
void printr(int s,int k){
printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n",s,k);
}
void init(){
MS(a,0);
MS(f,0);
scanf("%d%d",&n,&m);// kinds money
for(int i=1 ;i<=n; i++)
scanf("%d",&a[i]);
}
void work(){
for(int i=0 ;i<=m ;i++)
f[i][1] = 1;
for(int i=1; i<=n; i++)
{for(int k=m; k>=a[i] ;k--)
{
if(f[k][0]==f[k-a[i]][0]+1)
f[k][1] += f[k-a[i]][1];
else if(f[k][0] < f[k-a[i]][0]+1)
{
f[k][1] = f[k-a[i]][1];
f[k][0] = f[k-a[i]][0]+1;
}
}
}
if(!f[m][0])printw();
else printr(f[m][1],f[m][0]);
}
int main(){
scanf("%d",&t);
while(t--){
init();
work();
}
return 0;
}
大致也就要说这么多,感觉这几个题目对01背包有了一个很好的运用,同时也能加深对其的理解,以及提高了对有限制条件时的dp的写法的思维。