首页 > 代码库 > 【POJ1882】Stamps,灵活多重背包,详细题意!!!(题解也有)
【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*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; }
【POJ1882】Stamps,灵活多重背包,详细题意!!!(题解也有)