邮票问题
【问题描述】
给定一个信封,最多只允许粘贴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分,所有邮资的最大范围是MAX=10.
【样例输入】
从键盘输入一个文本文件的文件名,该文件第1行为最多粘贴的邮票数N;第2行为邮票种数m,以下m行各有一个数字,表示邮票的面值xi.如:
输入:
4
2
1
4
【样例输出】
1. 若最大范围为空,则在屏幕上输出MAX=0
2. 若最大范围不为空,则把结果输出到屏幕上.
输出:
MAX=10.
解题思路
本题可以看成是一个集合问题,即求在贴的邮票不多于n张的可满足条件的邮资集.集合问题的关键在于判定元素是否在集合中,对于本题而言,是判断某个邮资是否在贴不多于n张邮票可满足.设当前考虑的邮资值为max,最多允许贴n张邮票,记为(max,n).如果首先贴一张面值为xi的邮票,那么剩下的问题是(max-xi,n-1)的问题.如果(max-xi,n-1)可解,那么(max,n)问题也可解.
设f[max]表示邮资为max时所需最少的邮票数,f[MAX]=min{f[MAX-w[i]]+1}.当f[max]<=n时,问题(max,n)可解,否则无解.找到第一个不能集齐的邮票面值或者是第一个超过n张邮票的面值即可。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int w[110];//每个邮票的面值
int f[110];
int max;//枚举最大面值
int n,m,t;
const int inf=0x3f3f3f3f;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&w[i]);
}
memset(f,inf,sizeof(f));
f[0]=0;
while(1)
{
for(max=1;;++max)
{
for(int i=1;i<=m;i++)
{
if(max>=w[i])//能贴第i张邮票
{
if(f[max]>f[max-w[i]]+1)//不贴和贴谁用的张数少
f[max]=f[max-w[i]]+1;
}
}
if(f[max]==inf||f[max]>n)//第一个达不到的面值,或者超过了能贴的邮票总数
{
printf("%d\n",max-1);
break;//跳出面值循环
}
}
break;//跳出总循环
}
return 0;
}