父亲需要些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/