BZOJ3717 [PA2014] Pakowanie [状态压缩][DP]
Time Limit: 90 Sec Memory Limit: 256 MB
Description
你有n个物品和m个包。物品有重量,且不可被分割;包也有各自的容量。要把所有物品装入包中,至少需要几个包?
Input
第一行两个整数n,m(1<=n<=24,1<=m<=100),表示物品和包的数量。
第二行有n个整数
,分别表示物品的重量。
第三行有m个整数
,分别表示包的容量。
Output
如果能够装下,输出一个整数表示最少使用包的数目。若不能全部装下,则输出NIE。
Sample Input
4 3
4 2 10 3
11 18 9
Sample Output
2
解法
我最开始还在纠结为什么不用普通的背包做,因为普通的背包只有一个啊……= =
首先有一个贪心:尽可能使用容积大的背包,这样装得更多。
显然要压缩物品的数量。所以用状态 表示选定 集合中的物品最少使用的包的数目(因此 数组要初始化为 )。但是会发现这样的状态不好推,因为不知道下一个物品应该放在哪里。所以再来一个数组 表示选定集合 中的物品剩余的最大容积。如果下一个物品 的容积小于这个最大容积,那么 就和 共用 个包;否则如果第 个背包能装下第 个物品,那么j就放进这个背包里面, 与 中的物品共同使用了 个包。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 16777316
using namespace std;
int A[30],B[150],f[N],g[N];
bool cmp(int a,int b){return a>b;}
int main(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
for(int i=1;i<=m;i++)scanf("%d",&B[i]);
sort(B+1,B+m+1,cmp);
int nn=(1<<n)-1;
for(int i=1;i<=nn;i++)f[i]=m+1;
for(int i=1;i<=nn;i++){
for(int j=0;j<n;j++){
if(!(i&(1<<j)))continue;
int k=i^(1<<j);
if(g[k]>=A[j+1]){
//要使得使用的背包数量最小,或者数量相同时剩余的容积尽可能大
if(f[i]>f[k] || (f[i]==f[k] && g[i]<g[k]-A[j+1]))
f[i]=f[k],g[i]=g[k]-A[j+1];
}else if(B[f[k]+1]>=A[j+1]){
if(f[i]>f[k]+1 || (f[i]==f[k]+1 && g[i]<B[f[k]+1]-A[j+1]))
f[i]=f[k]+1,g[i]=B[f[k]+1]-A[j+1];
}
}
}
if(f[nn]<=m)printf("%d",f[nn]);else printf("NIE");
return 0;
}