题意:给你一些邮票面值的集合,让你选择其中一个集合,使得“能用不超过n枚集合中的邮票凑成的面值集合S中从1开始的最大连续面值”(即mex(S)-1)最大。如果有多解,输出集合大小最小的一个;如果仍有多解,输出面值从大到小排序后最小的一个。
少数能用bitset优化的dp问题之一。设bs[i]为用不超过i枚邮票能凑成的面值集合,正在放进的邮票面值为j,则有状态更新公式:$bs[i+1]=bs[i]|(bs[i]<<j)$,根据这个公式进行转移即可。
保存答案可以用vector,便于长度以及字典序的比较。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=20+5,M=10+2,inf=0x3f3f3f3f; 5 int n,m,k,ans; 6 vector<int> v1,v2; 7 bitset<1000+10> bs[M]; 8 9 bool cmp(vector<int>& v1,vector<int>& v2) { 10 if(v1.size()!=v2.size())return v1.size()<v2.size(); 11 for(int i=v1.size()-1; i>=0; --i)if(v1[i]!=v2[i])return v1[i]<v2[i]; 12 return 0; 13 } 14 15 int main() { 16 while(scanf("%d%d",&n,&m)&&n) { 17 v1.clear(),v2.clear(),ans=0; 18 while(m--) { 19 for(int i=0; i<M; ++i)bs[i].reset(); 20 bs[0].set(0),v2.clear(); 21 scanf("%d",&k); 22 while(k--) { 23 int x; 24 scanf("%d",&x); 25 for(int i=0; i<n; ++i)bs[i+1]|=bs[i]|(bs[i]<<x); 26 v2.push_back(x); 27 } 28 for(int i=0;; ++i)if(!bs[n].test(i)) { 29 if(i>ans||(i==ans&&cmp(v2,v1)))ans=i,v1=v2; 30 break; 31 } 32 } 33 printf("max coverage = %3d :",ans-1); 34 for(int i:v1)printf("%3d",i); 35 printf("\n"); 36 } 37 return 0; 38 }