codeup22345 邮票问题
时空限制 1000ms/128MB
题目描述
给定一个信封,最多只允许粘贴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. 若最大范围为空,则在屏幕上输出MAX=0
2. 若最大范围不为空,则把结果输出到屏幕上.
样例输入
4 2 1 4
样例输出
MAX=10
分析
本题可以看成是一个集合问题,即求在贴的邮票不多于n张的可满足条件的邮资集。
集合问题的关键在于判定元素是否在集合中,对于本题而言,是判断某个邮资是否在贴不多于n张邮票可满足。
这个判断问题可以用递归的方法解决。
设当前考虑的邮资值为maxv,最多允许贴n张邮票,记为(maxv,n).如果首先贴一张面值为xi的邮票,那么剩下的问题是(maxv-xi,n-1)的问题。如果(maxv-xi,n-1)可解,那么(maxv,n)问题也可解。
进一步, 这个递归的算法可以转化为递推的算法来解决。设f[value]表示邮资为value时所需最少的邮票数,f[maxv]=min{f[maxv-x[i]]+1}。当f[value]<=n时,问题(maxv,n)可解,否则无解。
递推算法可以描述为:
for (int i=1; i<=m; i++)
if (maxv>=x[i]){ //能贴第i张邮票
if (f[maxv]==0) f[maxv]=f[maxv-x[i]]+1; //没有贴
if (f[maxv]>f[maxv-x[i]]+1) f[maxv]=f[maxv-x[i]]+1; //贴少的
}
以样例数据为例:
value | f[value] |
0 | 0 |
1 | min{f[1-1]+1}=1 |
2 | min{f[2-1]+1}=2 |
3 | min{f[3-1]+1}=3 |
4 | min{f[4-1]+1,fs[4-4]+1}=1 |
5 | min{f[5-1]+1,fs[5-4]+1}=2 |
6 | min{f[6-1]+1,fs[6-4]+1}=3 |
7 | min{f[7-1]+1,fs[7-4]+1}=4 |
8 | min{f[8-1]+1,fs[8-4]+1}=2 |
9 | min{f[9-1]+1,fs[9-4]+1}=3 |
10 | min{f[10-1]+1,fs[10-4]+1}=4 |
11 | min{f[11-1]+1,fs[11-4]+1}=5 (>4) |
代码
#include<iostream>
using namespace std;
const int M = 105, N = 25600;
int x[M],f[N]={0}; //f[i] 为面额为i所贴邮票最少张数
int main(){
int n,m,maxv=0;
cin>>n>>m;
for (int i=1; i<=m; i++) cin>>x[i];
while (1){
maxv++; //邮票面额
for (int i=1; i<=m; i++)
if (maxv>=x[i]){ //能贴第i张邮票
if (f[maxv]==0) f[maxv]=f[maxv-x[i]]+1; //没有贴
if (f[maxv]>f[maxv-x[i]]+1) f[maxv]=f[maxv-x[i]]+1; //贴少的
}
if (f[maxv]==0 || f[maxv]>n){ //达不到面值,或超过了能贴的邮票总数
cout<<"MAX="<<maxv-1<<endl;
return 0;
}
}
}