题意
你有n个物品和m个包。物品有重量,且不可被分割;包也有各自的容量。要把所有物品装入包中,至少需要几个包?
Input
第一行两个整数n,m(1<=n<=24,1<=m<=100),表示物品和包的数量。
第二行有n个整数a[1],a[2],…,an,分别表示物品的重量。
第三行有m个整数c[1],c[2],…,cm,分别表示包的容量。
思路:
首先我们可以贪心地选取前几大的包,(由于物品数<=24,其实只用考虑前24个包即可)
考虑到24的数据 状压显得理所当然。
这题需要维护两个东西,
一个是zhi[s]表示装状态为s的物品需要的最小的包数,
但是这样没法转移,于是还要维护, h[s]表示对应zhi[s],最后一个包剩下的空间。
zhi[s]从zhi[t]而来(t为s任意去掉一个1),若zhi[t]+1值更小,更新,或者zhi[t]+1==zhi[s]值相同,h[t]更大,更新
可是为什么这样就可以了
考虑一个状态,其实在转移过程中,你考虑了这个状态的每一种放物品顺序
比如取物品2.4 在状态转移zhi[10] 时 肯定是从 zhi[2] 和zhi[8] 分别转移过来的
这就是和贪心的区别(感觉这就是状压的妙处)
#include<bits/stdc++.h>
using namespace std;
int n,m,h,h1,f,i1,i2,zhi[20000000],ret[20000000],w[20000000],sum[1000],a[100];
bool cmp(int a1,int b1)
{ return a1>b1;}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=m;i++) cin>>sum[i];
sort(sum+1,sum+1+m,cmp);
for (int i=1;i<1<<n;i++)
w[i]=w[i>>1]+1;
memset(zhi,0x3f,sizeof(zhi));
zhi[0]=0;
for (int i=1;i<1<<n;i++)
{ i2=i;
for (int j=w[i];j;j=w[i2])
{ f=0;
i2=i2-(1<<(j-1));
i1=i-(1<<(j-1));
//cout<<i<<' '<<i1<<' '<<i2<<endl;
if (a[j]<=ret[i1]) {h=zhi[i1]; h1=ret[i1]-a[j];}
else {h=zhi[i1]+1;
if (sum[h]<a[j] || h>m) f=1;
h1=sum[h]-a[j];}
if(!f)
{if (h<zhi[i]) { zhi[i]=h; ret[i]=h1;}
else { if (zhi[i]==h) ret[i]=max(ret[i],h1);}}
}
}
if (zhi[(1<<n)-1]<=m) cout<<zhi[(1<<n)-1]<<endl; else cout<<"NIE"<<endl;
}
小拓展以示区别 背包问题
放一个我觉得有代表意义的
多重背包问题:
多重背包问题描述:有编号分别为a,b,c的三件物品,它们的重量分别是1,2,2,它们的价值分别是6,10,20,他们的数目分别是10,5,2,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和?
多重背包和01背包、完全背包的区别:多重背包中每个物品的个数都是给定的,可能不是一个,绝对不是无限个。
有两种解法,解题思路:
- 作为一个新问题考虑,由于每个物品多了数目限制,因此初始化和递推公式都需要更改一下。初始化时,只考虑一件物品a时,f[1][j] = min{num[1], j/weight[1]}。 计算考虑i件物品承重限制为y时最大价值f[i][y]时,递推公式考虑两种情况,要么第 i 件物品一件也不放,就是f[i-1][y], 要么第 i 件物品放 k 件,其中 1 <= k <= (y/weight[i]),考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,即f[i][y] = max{f[i-1][y], (f[i-1][y-k*weight[i]]+k*value[i])}。 这里为什么不能像完全背包一样直接考虑f[i][y-weight[i]]+value[i]呢?因为这样不容易判断第 i 件物品的个数是否超过限制数量 num[i]
其实就是普通dp,前不久了解到它是NPC问题,而之所以我们能o(n*m)解决,是因为限制了背包容量大小。