【BZOJ3717】[PA2014]Pakowanie 状压DP

【BZOJ3717】[PA2014]Pakowanie

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

题解:一开始想到了栅栏那题,以为是爆搜+剪枝,结果狂TLE不止,后来发现是状压。。。

显然我们应该贪心的从容量较大的包开始选,然后用f[S]表示已经用过的物品状态为S时,最少用几个包,g[S]表示在用的包最少的前提下,最后一个包的最大剩余容量。

转移时用到了一些卡常小技巧~

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,N;
int f[1<<24],g[1<<24],w[1<<24],c[110];
int main()
{
	scanf("%d%d",&n,&m),N=(1<<n)-1;
	int i,j,k,S;
	for(i=0;i<n;i++)	scanf("%d",&w[i]);
	for(i=n-1;i>=0;i--)	w[1<<i]=w[i];
	for(i=1;i<=m;i++)	scanf("%d",&c[i]);
	sort(c+1,c+m+1);
	f[0]=m+1,g[0]=0;
	for(i=1;i<=N;i++)
	{
		for(j=i;j;j-=k)
		{
			k=j&-j,S=i^k;
			if(g[S]>=w[k]&&(f[S]>f[i]||(f[S]==f[i]&&g[S]>g[i]+w[k])))	f[i]=f[S],g[i]=g[S]-w[k];
			else	if(c[f[S]-1]>=w[k]&&(f[S]>f[i]+1||(f[S]==f[i]+1&&c[f[i]]>g[i]+w[k])))	f[i]=f[S]-1,g[i]=c[f[i]]-w[k];
		}
	}
	if(!f[N])	printf("NIE");
	else	printf("%d",m+1-f[N]);
	return 0;
}

 

原文地址:https://www.cnblogs.com/CQzhangyu/p/7787818.html