你的工作是对于输入的集合S去寻找集合中的第N个丑数。longint(signed 32-bit)对于程序是足够的。
27
这道题目我给出从最根本到高效的所有算法:
首先,最容易想到,不用动脑都可以想到的就是分解i的质因数是否是丑数,然后再inc(ans),判断ans=n的时候输出i。
代码:
var
n,m,i,k,tot,ans:Longint;
a:array[1..100] of Longint;
s:set of 1..100;
function fenjie:boolean;
var
x,y:Longint;
begin
x:=k;
y:=2;
while x>1 do
begin
while x mod y=0 do
begin
if not (y in s) then exit(false);
x:=x div y;
end;
inc(y);
end;
fenjie:=true;
end;
begin
readln(n,m);
for i:=1 to n do
begin
read(a[i]);
s:=s+[a[i]];
end;
tot:=0;
k:=2;
while tot<m do
begin
if fenjie then
begin
inc(tot);
ans:=k;
end;
inc(k);
end;
writeln(ans);
end.
但是上面算法的效率极低,只能得20分。
我们可以进一步优化,当我们把19个丑数列出来时:
num=2 3 4 5 6 7 8 9 10 12 14 15 16 18 20 21 24 25 27
tot=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
你会发现,其实
4是由两个2组成的
6是由2,3组成的
8是由2,4组成的
9是由3,3组成的
10是由2,5组成的...
这些所组成的数,都是由前面的丑数*a[k]得来的,所以可以得到如下的一个算法:
判断当前是由前面哪个丑数乘以a[k]得来的最小值且大于第i-1个丑数的丑数。
var
i,j,k,p,n,max:longint;
a:array[1..100] of longint;
f:array[0..100000] of longint;
function min(x,y:Longint):Longint;
begin
if x<y then exit(x) else exit(y);
end;
begin
readln(n,k);
for i:=1 to n do
read(a[i]);
f[0]:=1;
for i:=1 to k do
begin
max:=maxlongint;
for j:=1 to n do
for p:=1 to i do
if a[j]*f[p-1]>f[i-1] then max:=min(max,a[j]*f[p-1]); //取一个最小值max,但前提大于f[i-1]
f[i]:=max;
end;
writeln(f[k]);
end.
这样一来,大概可以得50分了,但是还可以再进一步优化:
还是刚刚模拟的数:
num=2 3 4 5 6 7 8 9 10 12 14 15 16 18 20 21 24 25 27
tot=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
我们先来看一下为什么上面的算法会慢,例如当判断第19个丑数的时候,你还要从第1个丑数开始判断,这样肯定慢了,因为第一个丑数2乘以任何a[i]都不可能大于第18个丑数,第二个、第三个、第四个也一样——27肯定是由第8个丑数与3构成的。还可以这样理解:当前a[2]=3,3与第4个丑数构成了第12个丑数,那么,请问,接下来的丑数构成,3与第4个丑数还有可能构成吗?3只有可能与第5个丑数构成,当然这不一定能构成,但至少可以判断3不可能与前4个丑数构成了。
所以可以得出如下结论:
判断当前第i个丑数是什么值之后,再判断是由哪个丑数乘以哪个a[i]构成的,然后再把i所可能构成的丑数的位数+1.以此类推,可以得到最优解,效率O(nk):
var
d:Array[1..100000] of Longint;
f:Array[1..100] of Longint;
a:array[1..100] of Longint;
k,n,i,j,tot,max:longint;
begin
readln(k,n);
for i:=1 to k do
begin
read(a[i]);
f[i]:=1;
end;
tot:=1;
d[1]:=1;
while tot<=n do
begin
inc(tot);
max:=maxLongint;
for i:=1 to k do
if (d[f[i]]*a[i]<max) and (d[f[i]]*a[i]>d[tot-1]) then max:=d[f[i]]*a[i];
for i:=1 to k do
if d[f[i]]*a[i]=max then inc(f[i]); //这里记录是由哪个构成的,然后就可以快速得出下一个丑数了。
d[tot]:=max;
end;
writeln(max);
end.