我之所以发题解(题意),就是因为百度搜索上发的太少了,而且还不清晰,所以:
题意:我直接拿数据说话吧(可以直接看下面的大号粗体字,如果你语文够好的话。)
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*log(p)),然后用计数器优化就变成了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;
}