题目描述
给定一个信封,最多只允许粘贴N(N<=100)张邮票,我们现在有M(M<=100) 种邮票,面值分别为:x1,x2,...,xm分(Xi<=255,为正整数),并假设各种邮票都有足够多张。
要求计算所能获得的邮资最大范围。即求最大值MAX,使在1—MAX之间的每一个邮票资值都得到。
例如,N=4,有2种邮票,面值分别为1分、4分,于是可以得到1—10分和12分、13分、16分邮资,由于得不到11分和15分,所以邮资的最大值范围为10。
输入格式:
输入文件stamps.in中读入数据,该文件第1行为最多粘贴的邮票张数N,第2行为邮票种数M,以下M行各有一个数字,表示邮票的面值Xi。
输出格式:
输出文件stamps.out:
1、若最大范围为空,则输出0
2、若最大范围不为空,则把结果输出到文件中。
输入样例#1:
4
2
1
4
输出样例#1
10
【解题分析】
暴力枚举,从面值1开始枚举所有的面值可能,如果某个面值不能出现,则它的前一个面值是最小面值。
如何判定一个面值是否存在呢?如果一个面值存在,那它的邮票数量必然少于n,方案可能不唯一,基于贪心的原则,我们可以记录面值为S的时候需要的最少的邮票数量,设d[i]表示面值为i的需要的最少邮票数,基于加法原理,我们可以对邮票数进行拆分,枚举所有可能d[i]=min{d[i-j]+d[j]},当d[i]无法求取就是断层点了。
显然,对于每一种邮票的面值,d[x]=1就是最小的邮票数,其他面值则预设无穷大即可。
【参考代码】
#include<iostream>
using namespace std;
int d[25505],n,m,x;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x;
d[x]=1; //单枚邮票面值
}
for(int i=1;i<=25500;i++){
if(d[i]==0) d[i]=1000; //预设无穷大
for(int j=1;j*2<i;j++)
d[i]=min(d[i],d[i-j]+d[j]);
if(d[i]>n){
cout<<i-1;
return 0;
}
}
}