邮票谜题

    父亲需要些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种方法有些抽象,需要理解递归的思想,但思路还是很巧妙的,可以做作解决问题的创新思维。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69908786/viewspace-2636578/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/69908786/viewspace-2636578/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值