UVa242 Stamps and Envelope Size

        题意:信封上最多贴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;
}


你可能感兴趣的:(dp,uva)