BZOJ3717 [PA2014] Pakowanie [状态压缩][DP]

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个整数 a [ 1 ] , a [ 2 ] , , a [ n ] ( 1 <= a [ i ] <= 10 8 ) ,分别表示物品的重量。
第三行有m个整数 c [ 1 ] , c [ 2 ] , , c [ m ] ( 1 <= c [ i ] <= 10 8 ) ,分别表示包的容量。

Output

如果能够装下,输出一个整数表示最少使用包的数目。若不能全部装下,则输出NIE。

Sample Input

4 3
4 2 10 3
11 18 9

Sample Output

2

解法

我最开始还在纠结为什么不用普通的背包做,因为普通的背包只有一个啊……= =

首先有一个贪心:尽可能使用容积大的背包,这样装得更多。

显然要压缩物品的数量。所以用状态 f [ s ] 表示选定 s 集合中的物品最少使用的包的数目(因此 f 数组要初始化为 M A X )。但是会发现这样的状态不好推,因为不知道下一个物品应该放在哪里。所以再来一个数组 g [ s ] 表示选定集合 s 中的物品剩余的最大容积。如果下一个物品 j 的容积小于这个最大容积,那么 j 就和 s 共用 f [ s ] 个包;否则如果第 f [ s ] + 1 个背包能装下第 j 个物品,那么j就放进这个背包里面, j s 中的物品共同使用了 f [ s ] + 1 个包。

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80579661