邮票谜题

来源:这里教程网 时间:2026-03-03 12:58:14 作者:

    父亲需要些1分、2分、3分、5分、10分的邮票,其中两种各买四张,另外的三种各买三张。我忘记是具体张数是如何分配的,只知道他给了我一些10分硬币, 金额刚好买这些邮票。 方法1:笛卡儿积 总共有5种邮票,每种的数目都为3或4.用笛卡儿积求出数目的所有组合,并且从中筛选出总面值可以被10整除的,并且数目4出现两次,数目3出现3次的那些组合。  with c as   (select 3 cnt from dual union all select 4 from dual),--邮票数目   stamps as   (select c1.cnt || c2.cnt || c3.cnt || c5.cnt || c10.cnt as all_cnt,--把5种邮票的数目串起来           c1.cnt + c2.cnt * 2 + c3.cnt * 3 + c5.cnt * 5 + c10.cnt * 10 as val,--总面值           '1*' || c1.cnt || '+2*' || c2.cnt || '+3*' || c3.cnt || '+5*' ||           c5.cnt || '+10*' || c10.cnt as result --用字符串表示的组合结果      from c c1, c c2, c c3, c c5, c c10)  select result || '=' || val    from stamps   where mod(val, 10) = 0  --总面值必须是10的倍数     and regexp_count(all_cnt, '3') = 3 --数目3出现了3次     and regexp_count(all_cnt, '4') = 2 ;--数目4出现了2次      方法2:把5种邮票的数据做一个全排列 因为数目仅有两种,全排列的结果中会有很多重复的,用distinct把重复值去除。 with stamps as    --构造出一个包含了所有邮票面值及所有张数的集合  (select rownum rn,          (case  when rownum in (1, 2) then 4 else 3  end) as cnt,--4张的有2行,3张的有2行          decode(rownum, 4, 5, 5, 10, rownum) val  --每种邮票的面值     from dual   connect by rownum <= 5), t1 as  --集合t1利用组合的方式求全排列,最后加上distinct去除重复结果  (select distinct replace(sys_connect_by_path(cnt, ','), ',') all_cnt     from stamps    where level = 5   connect by nocycle rn <> prior rn          and level < = 5), t2 as  --把求出的邮票张数和面值进行一一对应  (select id,           to_number(substr(all_cnt, stamps.rn, 1)) cnt, --把张数的一个排列拆开为5行          stamps.val  --每行对应的面值     from (select rownum id, all_cnt from t1),  --为每种排列取一个唯一的ID           stamps)   select * from ( select t2.id, cnt,val,        sum(cnt * val) over(partition by id) total   --用sum求出每种组合的总面值,id为每种组合的唯一标识,每个id对应t1的1行,t2的5行   from t2 where mod(total,10)=0 order by id,val; 方法3:用递归with with t(lvl,val,left_4,left_3,result) as (--结构:递归层数,总面值,数目为4的邮票种数,数目为3的邮票种数 select 0,0,2,3,'' from dual --初始化结果集:开始数目为4的邮票可有2种,数目为3的邮票可有3种 union all select t.lvl+1,  --加入一种邮票        t.val+s.price*cnt,  --总面值递增        left_4-decode(cnt,4,1,0),--如果加入的张数为4,则数目为4的邮票种数递减        left_3-decode(cnt,3,1,0),--如果加入的张数为3,则数目为3的邮票种数递减        t.result||' '||s.price||'*'||cnt   from t,        (select rownum price_id,decode(rownum,4,5,5,10,rownum) as price from dual connect by rownum<=5) s,--所有5种面值        (select 4 as cnt from dual union all select 3  from dual )  --每种面值可能的张数  where t.lvl+1 = s.price_id  --连接条件:新加入第n种邮票,n为1至5    and (cnt = 4 and left_4>0 or cnt =3 and left_3>0)  --如果种数未减至0,则该数目可以出现    )      select result||'='||val    from t    where lvl =5 and mod(val,10)=0;  --最后筛选条件:5种齐全而且总面值为10的整数倍 总结:前2种方法比较容易理解,第3种方法有些抽象,需要理解递归的思想,但思路还是很巧妙的,可以做作解决问题的创新思维。

相关推荐