【POJ1882】Stamps,灵活多重背包,详细题意!!!(题解也有)

我之所以发题解(题意),就是因为百度搜索上发的太少了,而且还不清晰,所以:


题意:我直接拿数据说话吧(可以直接看下面的大号粗体字,如果你语文够好的话。)

5
2
4 1 4 12 21
4 1 5 12 28
10
2
5 1 7 16 31 88
5 1 15 52 67 99
6
2
3 1 5 8
4 1 5 7 8
0
第一行 5 是最多能拿5张邮票,第二行2是有两组邮票。

第三行4是该组有4张邮票,然后4个数是4个面额。

第五行“10”开始就是下一组数据了,输入到“0”为止。

现在数据说完了,拿第一组数据说题意。

两组邮票中每组都可以任意取不多于5张邮票,组成一个新的面额,显然可以取到1~71种新面额,而之后可能也能取到,但是就不连续了,就不算了。

现在问第几组能取到最大的连续面额。面额是多少,并输出该组邮票基础面额!

如果两组邮票最大连续面额相同,输出基础面额个数少的那个!

如果两组邮票基础面额个数相同,输出基础面额最大值最小的那个!

如果两组邮票基础面额最大值相同,输出前一组!

(输入面额时保证面额从小到大。)


题解:

每一组做一遍背包(DP)看最大连续和,然后输出符合要求的那个!

背包过程:多重背包。首先讲一下三种多重背包做法及时间复杂度(拆分物品转成01)是O(体积*物品总数(∑每个物品类型有多少个)),姑且认为每种物品个数相同,则时间复杂度是O(m*n*p),然后二进制拆分就变成了O(m*n*logp),然后用计数器优化就变成了O(m*n),

计数器优化:http://blog.csdn.net/vmurder/article/details/39473505

二进制优化:http://blog.csdn.net/vmurder/article/details/39472419


然后这道题是共用数量,所以计数器共用一下就好了(不过需要更新)

水水的代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 11
#define L 101
using namespace std;
struct Fiona
{
	int w[N],num,length;
	bool operator < (const Fiona &a)const
	{
		if(length!=a.length)return length>a.length;
		if(num!=a.num)return num<a.num;
		return w[num]<a.w[num];
	}
}s[N];

int n,m;
int cnt[N*L];
bool f[N*L];
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k,u,temp;
	while(scanf("%d",&m),m)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&s[i].num);
			for(j=1;j<=s[i].num;j++)scanf("%d",&s[i].w[j]);
			/*输入数据保证了升序*/
		}
		for(i=1;i<=n;i++)
		{
			memset(f,0,sizeof(f));
			f[0]=1;
			for(j=1;j<=s[i].num;j++)/*枚举物品*/
			{
				//memset(cnt,0,sizeof(cnt));
				temp=s[i].w[j]*m;
				for(k=s[i].w[j];k<=temp;k++)if(f[u=k-s[i].w[j]]&&cnt[u]<m)
				{
					if(!f[k]||cnt[k]>cnt[u]+1)
					{
						f[k]=1;
						cnt[k]=cnt[u]+1;	
					}
				}
			}
			for(s[i].length=0;f[s[i].length+1];s[i].length++);
		}
		sort(s+1,s+n+1);
		printf("max coverage = %d :",s[1].length);
		for(i=1;i<=s[1].num;i++)printf(" %d",s[1].w[i]);
		puts("");
	}
	return 0;
}


你可能感兴趣的:(题解,计数器,多重背包,POJ1882,题意)