邮票问题
题目链接:https://www.luogu.org/problemnew/show/P2725
http://www.joyoi.cn/problem/tyvj-1199
http://tyvj.cn/p/1199
题目限制
时间限制 | 内存限制 | 评测方式 | 题目来源 |
1000ms | 131072KiB | 标准比较器 | Local |
题目描述
给定一个信封,最多只允许粘贴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
输入格式
第一行为最多粘贴的邮票张数n。第二行为邮票种数m。以下m行各有一个数字表示邮票的面值。
本地测试的输入格式:
第一行:m,n的值,中间用一空格隔开。
第二行:A[1..m](面额),每个数中间用一空格隔开。
输出格式
仅一个数,最大的max的值。
算法分析:
从面额1开始,建立递推关系方程。比如:面额1,2,4只用1张邮票行了,面额3可以表示为面额1,2的邮票和1+1=2张邮票,面额5有两种表示方式min(面额1+面额4,面额2+面额3)邮票数目……照此类推,递推关系方程不难建立,以下是递推的一种方法:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 freopen("stamp_data/STAMP14.in","r",stdin); 7 freopen("stamp_data/STAMP14.txt","w",stdout); 8 int n,m,i,j,c[256]={0},a[31001]={0}; 9 int b1=1; 10 11 scanf("%d%d",&m,&n);//local test 12 //scanf("%d%d",&n,&m);//online judge 13 for(i=1;i<=m;i++) 14 { 15 scanf("%d",&c[i]); 16 if(c[i]==1) b1=0; 17 } 18 19 if(b1==1) printf("0\n"); 20 else 21 { 22 i=1; a[1]=1;//a[i]=x表示构造i这个值用x张邮票 23 do 24 { 25 i++; 26 for(j=1;j<=m;j++) 27 { 28 if( (i%c[j]==0)&&( (i/c[j]<a[i]) || (a[i]==0) )) 29 a[i]=i/c[j]; //判断它能否被题目给定面额整除 30 } 31 for(j=1;j<=i/2;j++) //寻找 1<= j <=i使得a[j]+a[i-j]值最小 32 if(a[j]+a[i-j]<a[i]) a[i]=a[j]+a[i-j]; 33 }while((a[i]<=n)&&(a[i]!=0)); 34 printf("%d\n",i-1); 35 } 36 return 0; 37 }
这种递推方法原理是:对于某种要求得到的面额,判断它能否被题目给定面额整除,再寻找(1<=j<=i),使A[j]+A[i-j]值最小,求出凑成某种面额最少邮票数。
这种递推方法虽然简单,由于1<=邮票面额<=255,1<=n<=100,因此MAX值最多可达到25500,25500次循环里必定还有嵌套循环,因此算法不加优化,很难在规定时间内得出最优值。这就需要递推的算法优化。
进一步优化
何不将用m种面额邮票作循环,建立递推关系式:A[i]=min(A[i-C[j]]+1),于是当取到极限值时,程序减少了约1.6*10^8次循环,递推优化作用不言而喻。
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 freopen("stamp_data/STAMP0.in","r",stdin); 7 freopen("stamp_data/STAMP0.txt","w",stdout); 8 int x[256]={0},pieces[30001]={0}; 9 int n,m,i; 10 int maxX=0; 11 scanf("%d%d",&m,&n); //local test 12 //scanf("%d%d",&n,&m); //online judge 13 for(i=1;i<=m;i++) 14 scanf("%d",&x[i]); 15 do 16 { 17 maxX++; 18 for(i=1;i<=m;i++) 19 { 20 if(maxX-x[i]>=0) 21 { 22 if(pieces[maxX]==0) pieces[maxX]=pieces[maxX-x[i]]+1; 23 if(pieces[maxX]>pieces[maxX-x[i]]+1) pieces[maxX]=pieces[maxX-x[i]]+1; 24 } 25 } 26 if(pieces[maxX]==0 || pieces[maxX]>n) 27 { 28 printf("%d\n",maxX-1); 29 break; 30 } 31 }while(1); 32 return 0; 33 }