邮票问题,副答案
1、邮票问题
设有已知面额的邮票m种,每种有n张,用总数不超过n张的邮票,能从面额1开始,最多连续组成多少面额.(1<=m<=100,1<=n<=100,1<=邮票面额<=255) 输入:n m A[1..m](面额) 输出:MAX=(最大面额值) Input1:4 1 2 4 Output1:10 一看到这个题目,给人的第一感觉是用回溯算法,从面额1开始,每种面额都用回溯进行判断,算法复杂度并不高,但是当m,n取到极限值100时,程序明显超时,因此,回溯算法在这里并不可取. 能否用递推完成呢?我们有一个思路:从面额1开始,建立递推关系方程,就用范例来说吧,面额1,2,4只用1张邮票行了,面额3可以表示为面额1,2的邮票和1+1=2,面额5有两种表示方式MIN(面额1+面额4,面额2+面额3),照此类推,递推关系方程不难建立,由于1<=邮票面额<=255,1<=n<=100,因此MAX值最多可达到25500,25500次循环里必定还有嵌套循环,因此算法不加优化,很难在规定时间内得出最优值.这就需要递推的算法优化. 一味递推不寻求算法优化,速度较之搜索提高不少,但一旦数据规模过大,很难在规定时间内得出最优值.就拿邮票问题来说,以下是递推的一种方法:
{$A+,B-,D-,E+,F-,G-,I-,L-,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y-} {$M 16384,0,655360}
program stamps;
var f,fout:text; n,m,i,j,k:integer;
c:array [1..255] of integer; {面额}
a:array [1..31000] of integer; {递推数组}
bl:boolean;
procedure readfile; {读文件}
begin
assign(f,'STAMP.DAT');
reset(f); assign(fout,'STAMP.OUT');
rewrite(fout);
readln(f,n);
readln(f,m);
bl:=true;
for i:=1 to m do
begin
readln(f,c[i]);
if c[i]=1 then bl:=false;
end;
close(f);
end;
procedure work;
begin
if bl=true then write(fout,'MAX=0') {不存在面额1时输出无解}
else begin
i:=1; a[i]:=1;
/**************************************************/
repeat
i:=i+1;
for j:=1 to m do
if ((i mod c[j]=0) and ((i div c[j])<a[i]))
or (a[i]=0) {判断它能否被题目给定面额整除} then
a[i]:=i div c[j];
for j:=1 to trunc(i/2) do
if (a[j]+a[i-j]<a[i]) then a[i]:=a[j]+a[i-j]; {寻找(1<=j<=i),使A[j]+A[i-j]值最小}
until (a[i]>n) or (a[i]=0);
/**************************************************/
write(fout,'MAX=',i-1); {输出}
end;
close(fout);
end;
begin
readfile;
work;
end
两个/**************************************************/间的内容我看不明白,能解释一下吗?