[BZOJ3717][PA2014]Pakowanie解题报告
你有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),分别表示包的容量。
如果能够装下,输出一个整数表示最少使用包的数目。若不能全部装下,则输出NIE。
Sample Input4 3
4 2 10 3
11 18 9
Sample Output
2
我本来是打算以后都不贴原题的不过为了方便大家搜索到我的博客还是贴了原题。对于这道题最开始就往动态规划的方向想了,然后发现状态可以压缩,于是大概就构建出了状压dp的思路。
朴素的dp[s][i]会MLE,就考虑降维。于是有了状态S(二进制压缩物品取放状态)。然后考虑到先用小的包不会优于先用大的包(这里可以分类讨论一下,不展开说明),就贪心先取大的包。
于是考虑到状态dp[s]表示状态s时最少需要几个包,g[s]表示在状态s下,最后一个包剩余的空间。
递推的方式就很好想了,对于每个状态S,都枚举K,使K比S少取一个包且K取得包S都取了。那么就可以从dp[K]递推到dp[S]。递推的方式有两种,一种是用了新的包,一种是没有用。如果dp[k]等于dp[s]递推过来的值的话,就根据最后剩余的空间来判断状态的优劣。
考虑初始化,dp[0]初始化为0,dp[1~maxn]初始化为INF,g初始化成0。
话不多说,放代码了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline void read(int &x){ x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=getchar();} x*=f; } const int maxn=16777316; int M,N; int ob[30],bg[150]; int dp[maxn],g[maxn]; bool cmp(int a,int b) { return a>b; } int main() { read(N);read(M); const int top=(1<<N)-1; for(int i=1;i<=N;i++) read(ob[i]); for(int i=1;i<=M;i++) read(bg[i]); sort(bg+1,bg+M+1,cmp); for(int i=1;i<=top;i++) dp[i]=M+1; for(int i=1;i<=top;i++) { for(int j=1;j<=N;j++){ if(!(i&(1<<(j-1)))) { continue ; } int k=i^(1<<(j-1)); if(g[k]>=ob[j]){ if(dp[i]>dp[k]||(dp[i]==dp[k]&&g[i]<g[k]-ob[j])) { dp[i]=dp[k]; g[i]=g[k]-ob[j]; } } else if(bg[dp[k]+1]>=ob[j]){ if(dp[i]>dp[k]+1||(dp[i]==dp[k]+1&&g[i]<bg[dp[k]+1]-ob[j])) { dp[i]=dp[k]+1; g[i]=bg[dp[k]+1]-ob[j]; } } } } if(dp[top]<=M) printf("%d",dp[top]); else printf("NIE"); }