首页 > 代码库 > POJ 3093 Margaritas on the River Walk (0-1背包变形)

POJ 3093 Margaritas on the River Walk (0-1背包变形)

这题目的思路很巧妙,什么情况下剩下的所有物品都放不下呢?就是当前剩余物品中最小的那个也放不下。所以,先把物品按照容量从小到大排序,依次枚举当前背包为放不下的最小物品的情况。

对于当前物品i,必有1到i-1的所有物品都放进去,这时候比i大的物品谁放谁不放是不确定的。转换成0-1背包问题:把前i-1个物品都放进去以后,得到空间为tsum - sum[i-1](前缀和)的包,只要从第i+1到第n个物品中拿出一个方案填充这个包使得剩余体积小于第i个物品的体积就可以了,把总方案数累加就是结果!

注意特殊情况:当排序后最小的物品也无法放入时,直接返回0,因为dp[0]初始化为1,

代码:

/*
poj     3093
232K	0MS
*/
#include<cstdio>
#include<algorithm>
#include<iostream>

#define MAXN 1005

using namespace std;

int n, m, wei[35], dp[MAXN], sum[MAXN];

__int64 Bag()
{
	memset(sum, 0, sizeof(sum));
	sort( wei + 1, wei + n + 1 );
	if(wei[1] > m)	//因为很可能有最小的也放不下这种情况,而dp【0】是初始化为1的,会输出1 
		return 0;
	for(int i = 1;i <= n;i ++)
		sum[i] = sum[i-1] + wei[i];
	__int64 ans = 0;
	for(int i = 1;i <= n;i ++)
	{
		if(sum[i - 1] > m)
			break;
		memset(dp, 0, sizeof(dp));
		dp[sum[i-1]] = 1;
		for(int j = i + 1;j <= n;j ++)
			for(int k = m; k >= sum[i - 1] + wei[j]; k --)
				dp[k] += dp[k - wei[j]];

		for(int j = m - wei[i] + 1;j <= m;j ++)
			ans += dp[j];
	}
	return ans;
}

int main()
{
	int Cases;
	cin>>Cases;
	for(int cnt = 1; cnt <= Cases; cnt ++)
	{
		cin >> n >> m;
		for(int i = 1;i <= n;i ++)
			cin>> wei[i];
		cout << cnt << " " << Bag() <<endl;
	}
	return 0;
}