【USACO题库】3.1.3 Humble Numbers丑数

题目描述


对于一给定的素数集合 S = {p1, p2, ..., pK}, 来考虑那些质因数全部属于S 的数的集合。这个集合包括,p1, p1p2, p1p1, 和 p1p2p3 (还有其它)。这是个对于一个输入的S的丑数集合。
注意:我们不认为1 是一个丑数。

你的工作是对于输入的集合S去寻找集合中的第N个丑数。longint(signed 32-bit)对于程序是足够的。


INPUT FORMAT
第 1 行:二个被空间分开的整数:K 和 N , 1<= K<=100 , 1<= N<=100,000.
第 2 行:K 个被空间分开的整数:集合S的元素
SAMPLE INPUT (file humble.in) 
4 19
2 3 5 7

OUTPUT FORMAT
单独的一行,写上对于输入的S的第N个丑数。
SAMPLE OUTPUT (file humble.out)

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.








你可能感兴趣的:(【USACO题库】3.1.3 Humble Numbers丑数)