题意:信封上最多贴S张邮票。有N个邮票集合,每个集合有不同的面值。问哪个集合的最大连续邮资最大,输出最大连续邮资和集合元素。最大连续邮资是用S张以内邮票面值凑1,2,3...到n+1凑不出来了,最大连续邮资就是n。如果不止一个集合结果相同,输出集合元素少的,如果仍相同,输出最大面值小的。
思路:DP。DP[i][j]。i表示需要的邮资,j表示可用邮票数,数组存的是bool类型值,表示能否凑出。如果存在DP[i-某种面值][j-1]为真,那么DP[i][j]为真。下面是记忆化搜索的代码。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <memory.h>
#include <vector>
#include <queue>
#include <stack>
#include <ctype.h>
#define INF 1000000
using namespace std;
int stamp[12][12];
int DP[1110][12];
bool fun(int set,int c,int n){
if(DP[c][n]!=-1)return DP[c][n];
if(!c){
DP[c][n]=true;
return true;
}
if(!n){
DP[c][n]=false;
return false;
}
for(int i=1;i<=stamp[set][0];i++){
if(c>=stamp[set][i]&&fun(set,c-stamp[set][i],n-1)){
DP[c][n]=true;
return true;
}
}
DP[c][n]=false;
return false;
}
int cmp(int a,int b){//比较最大连续邮资相同的集合
if(stamp[a][0]<stamp[b][0])return a;
if(stamp[b][0]<stamp[a][0])return b;
for(int i=stamp[a][0];i>0;i--){
if(stamp[a][i]<stamp[b][i])return a;
if(stamp[b][i]<stamp[a][i])return b;
}
return a;
}
int main(){
int s;
while(cin>>s){
if(!s)break;
int N;
cin>>N;
int ans=0;
int key=0;
for(int i=1;i<=N;i++){
cin>>stamp[i][0];
for(int j=1;j<=stamp[i][0];j++){
cin>>stamp[i][j];
}
}
for(int i=1;i<=N;i++){
memset(DP,-1,sizeof(DP));
int k=0;
for(int j=1;;j++){
if(fun(i,j,s)){
k=j;
}else{
break;
}
}
if(k==ans){
key=cmp(key,i);
}
if(k>ans){
ans=k;
key=i;
}
}
printf("max coverage =%4d :",ans);
for(int i=1;i<=stamp[key][0];i++){
printf("%3d",stamp[key][i]);
}
printf("\n");
}
return 0;
}