滋润(状压dp)

滋润

Description
你有n个物品和m个包。物品有重量,且不可被分割;包也有各自的容量(<=10^8)。要把所有物品装入包中,至少需要几个包?

Input
第一行两个整数n,m(1<=n<=24,1<=m<=100),表示物品和包的数量。
第二行有n个整数a[1],a[2],…,an,分别表示物品的重量。
第三行有m个整数c[1],c[2],…,cm,分别表示包的容量。

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

Sample Input
4 3
4 2 10 3
11 18 9

Sample Output
2

分析:考虑状压dp。设f[x]表示选取物品状态为x的最小背包数量,g[x]为选取物品状 态为x,背包数量为f[x]时最后一个包剩余的空间,可以发现复杂度瓶颈在于枚举x中不为1的位,这个用树状数组时调用的 lowbit就可以优化到O(2^n)

代码

#include 
#include 
#define N 1 << 24
using namespace std;

int f[N],g[N],a[N],b[505];
int n,m,low;

int cmp(int x, int y){return x > y;}

int main()
{
	freopen("wilgotne.in","r",stdin);
	freopen("wilgotne.out","w",stdout);
	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);
	for (int i = n; i >= 1; i--)
		a[1 << (i - 1)] = a[i];
	int mx = 1 << n;
	for (int i = 1; i < mx; i++)
	{
		f[i] = m + 1;
		for (int j = i; j > 0; j -= low)
		{
			low = j & (-j);
			int k = i - low;
			if (a[low] <= g[k] && (f[k] < f[i] || (f[k] == f[i] && g[k] - a[low] > g[i]))) 
				f[i] = f[k], g[i] = g[k] - a[low];
			else if ((f[k] + 1 < f[i] && b[f[k] + 1] >= a[low]) || (f[k] + 1 == f[i] && b[f[k] + 1] - a[low] > g[k]))
				f[i] = f[k] + 1, g[i] = b[f[k] + 1] - a[low];
		}
	}
	if (f[mx - 1] > m) f[mx - 1] = -1;
	printf("%d", f[mx - 1]);
}

你可能感兴趣的:(状压dp)