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

本文解析了BZOJ3717题目的解题思路,采用状态压缩动态规划的方法来解决物品打包问题。通过合理利用背包容量,实现物品的有效分配,并求出最少使用背包数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Output

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

Sample Input

4 3
4 2 10 3
11 18 9

Sample Output

2

解法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值