01背包
特点:每种物品最多可放一次
样题:
题目:有N件物品和一个容量为V的背包。第i件物品的费用是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。
分析:每种物品都有两种状态,放或者不放,那么只要比较放所得的价值与不放背包内原有价值就能得到最大价值
二维数组:f[i][j]表示前i件物品放入容量为j的背包可以获得的最大价值
for i=1...n
for j=0...c
if j>=w[i]
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
//不放价值为dp[i ][j ]=dp[i-1 ][j ],前i-1件物品放入容量为j的背包所获价值
//放则dp[i ][j ]=dp[i-1 ][j-w[i ]]+v[i],前i-1件物品放入容量为j-w[i]的背包的最大价值加上第i件物品的价值v[i]
一维数组:f[j]表示容量为j的背包可以获得的最大价值
for i=1. .N
for j=V..0
f[j]=max {f[j],f[j-w[i]]+v[i]};
例题:
思路:每种物品只有放和不放两种状态,不放则背包内价值为f[i-1][j],放了则为f[i-1][j-w[i]]+v[i],意思是前i-1件物品放入容量为j-w[i]的背包的最大价值加上第i件物品的价值v[i],取两者最大值为最大价值
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std ;
int v[1005 ],w[1005 ];
int dp[1005 ][1005 ];
int main(){
int t,n,V,i,j;
scanf ("%d" ,&t);
while (t--)
{
scanf ("%d%d" ,&n,&V);
for (i=1 ;i<=n;i++)
scanf ("%d" ,&v[i]);
for (i=1 ;i<=n;i++)
scanf ("%d" ,&w[i]);
for (j=0 ;j<=V;j++)
dp[0 ][j]=0 ;
for (i=1 ;i<=n;i++)
{
for (j=0 ;j<=V;j++)
{
if (j>=w[i])
{
dp[i][j]=max(dp[i-1 ][j],dp[i-1 ][j-w[i]]+v[i]);
}
else
dp[i][j]=dp[i-1 ][j];
}
}
printf ("%d\n" ,dp[n][V]);
}
}
思路:这个题目稍有不同,显然当余额为5的时候去买那最大价值的物品才能使卡内余额最少,我们不妨直接先拿出5块,也去掉最大价值的物品,这样就是简单的01背包了,这里我采用了一维数组,f[j]为容量为j的背包所能放入的最大价值
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
int price[1005 ],f[1005 ];
int main()
{
int n,j,i,m,Max;
while (scanf ("%d" ,&n)!=EOF&&n)
{
memset (price,0 ,sizeof (price));
memset (f,0 ,sizeof (f));
for (i=1 ;i<=n;i++)
scanf ("%d" ,&price[i]);
scanf ("%d" ,&m);
if (m<5 ){
printf ("%d\n" ,m);
continue ;
}
sort(price,price+n+1 );
Max=price[n];
for (i=1 ;i<n;i++)
{
for (j=m-5 ;j>=price[i];j--)
{
f[j]=max(f[j],f[j-price[i]]+price[i]);
}
}
printf ("%d\n" ,m-Max-f[m-5 ]);
}
}
完全背包:
特点:每种物品都可以放任意次
样题:
题目:有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
伪代码:
for i=1. .N
for j=0. .V
f[j]=max {f[j],f[j-w[i]]+c[i]}
例题:
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
int p[50005 ],w[10005 ];
int f[10005 ];
int main()
{
int t,e,g,n,i,j;
scanf ("%d" ,&t);
while (t--)
{
scanf ("%d%d" ,&e,&g);
scanf ("%d" ,&n);
for (i=1 ;i<=n;i++)
scanf ("%d%d" ,&p[i],&w[i]);
f[0 ]=0 ;
for (i=1 ;i<=g-e;i++)
f[i]=2e9 ;
for (i=1 ;i<=n;i++)
{
for (j=0 ;j<=g-e;j++)
{
if (j>=w[i])
f[j]=min(f[j],f[j-w[i]]+p[i]);
}
}
if (f[g-e]>=2e9 )
printf ("This is impossible.\n" );
else
printf ("The minimum amount of money in the piggy-bank is %d.\n" ,f[g-e]);
}
}
多重背包:
特点:每种物品最多取c[i]件
样题:
题目:有N种物品和一个容量为V的背包。第i种物品最多有c[i]件可用,每件费用是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
分析:这个就相当于01背包和完全背包的综合,不止取一件,也取不了任意件。所以我们可以用二进制思想将其转化为01背包问题
方法:将第i件物品用二进制转化为若干件物品,每件物品有一个系数,系数为1,2,4,…,2^(k-1),c[i]-2^k+1,且k是满足c[i]-2^k+1>0的最大整数,因为2^k-1为前k-1的和。如13,转化为1,2,4,6,那么就把第i件分成了k件,物品的费用和价值均是原来的费用和价值乘以这个系数
核心代码:
for (i=1 ;i<=m;i++)
{
for (k=1 ;k*2 -1 <=c[i];k=k*2 )
for (j=n;j>=p[i]*k;j--)
{
f[j]=max (f[j],f[j-p[i]*k]+w[i]*k);
}
for (j=n;j>=p[i]*(c[i]-k+1 );j--)
f[j]=max (f[j],f[j-p[i]*(c[i]-k+1 )]+w[i]*(c[i]-k+1 ));
}
代码:
using namespace std;
int p[105 ],w[105 ],c[105 ];
int f[105 ];
int main()
{
int t,m ,n,i,j,k;
scanf("%d " ,&t);
while (t--)
{
scanf("%d %d " ,&n,&m );
for (i=1 ;i<=m ;i++)
scanf("%d %d %d " ,&p[i],&w[i],&c[i]);
memset(f,0 ,sizeof(f));
for (i=1 ;i<=m ;i++)
{
for (k=1 ;k*2 -1 <=c[i];k=k*2 )
for (j=n;j>=p[i]*k ;j--)
{
f[j]=max(f[j],f[j-p[i]*k ]+w[i]*k );
}
for (j=n;j>=p[i]*( c[i]-k+1 );j--)
f[j]=max(f[j],f[j-p[i]*( c[i]-k+1 )]+w[i]*( c[i]-k+1 ));
//printf ("%d \n" ,f[j]);
}
while (n--)
if (f[n+1 ]){
printf ("%d \n" ,f[n+1 ]);
break ;
}
}
}
代码:
using namespace std;
int c[10 ],s =1 ;
int f[200005 ];
int main()
{
do
{
int flag=0 ,sum=0 ,i,k,v,j;
for (i=1 ;i<=6 ;i++){
scanf("%d " ,&c[i]);
if (c[i]!=0 )
flag=1 ;
}
if (flag){
sum=c[1 ]*1 +c[2 ]*2 +c[3 ]*3 +c[4 ]*4 +c[5 ]*5 +c[6 ]*6 ;
memset(f,0 ,sizeof(f));
if (sum%2 )
{
printf ("Collection #%d :\n" ,s );
printf ("Can't be divided.\n\n" );
}
else {
v=sum/2 ; // 比较特殊,因为看是否能平分,所以只需考虑一半
for (i=1 ;i<=6 ;i++)
{
for (k=1 ;k*2 -1 <=c[i];k=k*2 )
for (j=v;j>=i*k ;j--){
f[j]=max(f[j],f[j-i*k ]+i*k );
//printf ("%d %d \n" ,j,f[j]);
}
//printf ("\n" );
for (j=v;j>=i*( c[i]-k+1 );j--){
f[j]=max(f[j],f[j-i*( c[i]-k+1 )]+i*( c[i]-k+1 ));
//printf ("%d %d \n" ,j,f[j]);
}
}
if (f[v]==v)
{
printf ("Collection #%d :\n" ,s );
printf ("Can be divided.\n\n" );
}
else
{
printf ("Collection #%d :\n" ,s );
printf ("Can't be divided.\n\n" );
}
}
}
else
break ;
}while (s ++);
return 0 ;
}