它相当于把S集合中的n个元素a1 ,a2,……,an 放入k个(0<k≤n<30=无标号的盒子中,使得没有一个盒子为空请你确定n个元素a1 ,a2 ,……,an 放入k个无标号盒子中去的划分数S(n,k)w【【输入样例输入样例】】setsub.inw 23 7w【【输出样例输出样例】】setsub.outw 43826419991173052021/8/2216w【【算法分析算法分析】】w 先举个例子,设S={1,2,3,4},k=3,不难得出S有6种不同的划分方案,即划分数S(4,3)=6,具体方案为:w{1,2}∪{3}∪{4} {1,3}∪{2}∪{4} {1,4}∪{2}∪{3}w{2,3}∪{1}∪{4} {2,4}∪{1}∪{3} {3,4}∪{1}∪{2}w 考虑一般情况,对于任意的含有n个元素a1 ,a2,……,an的集合S,放入k个无标号的盒子中去,划分数为S(n,k),我们很难凭直觉和经验计算划分数和枚举划分的所有方案,必须归纳出问题的本质其实对于任一个元素an,则必然出现以下两种情况:w 1、{an}是k个子集中的一个,于是我们只要把a1,a2,……,an-1 划分为k-1子集,便解决了本题,这种情况下的划分数共有S(n-1,k-1)个;w 2、{an}不是k个子集中的一个,则an必与其它的元素构成一个子集。
则问题相当于先把a1,a2,……,an-1 划分成k个子集,这种情况下划分数共有S(n-1,k)个;然后再把元素an加入到k个子集中的任一个中去,共有k种加入方式,这样对于an的每一种加入方式,都可以使集合划分为k个子集,因此根据乘法原理,划分数共有k * S(n-1,k)个2021/8/2217w 综合上述两种情况,应用加法原理,得出n个元素的集合{a1,a2,……,an}划分为k个子集的划分数为以下递归公式:S(n,k)=S(n-1,k-1) + k * S(n-1,k) (n>k,k>0)w 下面,我们来确定S(n,k)的边界条件,首先不能把n个元素不放进任何一个集合中去,即k=0时,S(n,k)=0;也不可能在不允许空盒的情况下把n个元素放进多于n的k个集合中去,即k>n时,S(n,k)=0;再者,把n个元素放进一个集合或把n个元素放进n个集合,方案数显然都是1,即k=1或k=n时,S(n,k)=1w 因此,我们可以得出划分数S(n,k)的递归关系式为:w S(n,k)=S(n-1,k-1) + k * S(n-1,k) (n>k,k>0)w S(n,k)=0 (nw using namespace std;w w int s(int n, int k) //数据还有可能越界,请用高精度计算w {w if ((n < k) || (k == 0)) return 0; //满足边界条件,退出w if ((k == 1) || (k == n)) return 1;w return s(n-1,k-1) + k * s(n-1,k); //调用下一层递归w }w w int main()w {w int n,k;w cin >> n >> k;w cout << s(n,k);w return 0;w }2021/8/2219w【【例例6 6】】数的计数数的计数(Noip2001)(Noip2001)w【【问题描述问题描述】】w我们要求找出具有下列性质数的个数(包括输入的自然数n)。
先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理:w不作任何处理;w在它的左边加上一个自然数,但该自然数不能超过原数的一半;w加上数后,继续按此规则进行处理,直到不能再加自然数为止w输入:输入:自然数n(n≤1000)w输出:输出:满足条件的数w【【输入样例输入样例】】w 6 满足条件的数为 6 (此部分不必输出)w 16w 26w 126w 36w 136w【【输出样例输出样例】】w 62021/8/2220【方法一】【方法一】w用递归,f(n)=1+f(1)+f(2)+…+f(div/2),当n较大时会超时,时间应该为指数级参考程序】【参考程序】w#includewusing namespace std;wint ans;wvoid dfs(int m) //统计m所扩展出的数据个数w{w int i;w ans++; //每出现一个原数,累加器加1;w for (i = 1; i <= m/2; i++) //左边添加不超过原数一半的自然数,作为新原数w dfs(i); w}wint main()w{w int n;w cin >> n;w dfs(n);w cout << ans;w return 0;w} 2021/8/2221w【方法二】【方法二】::用记忆化搜索,实际上是对方法一的改进。
设h[i]表示自然数i满足题意三个条件的数的个数如果用递归求解,会重复来求一些子问题例如在求h[4]时,需要再求h[1]和h[2]的值现在我们用h数组记录在记忆求解过程中得出的所有子问题的解,当遇到重叠子问题时,直接使用前面记忆的结果w【参考程序】【参考程序】w#includewusing namespace std;wint h[1001];wvoid dfs(int m)w{w int i;w if (h[m] != -1) return; //说明前面已经求得h[m]的值,直接引用即可,不需要再递归w h[m] = 1; //将h[m]置为1,表示m本身为一种情况w for (i = 1; i <= m/2; i++)w {w dfs(i);w h[m] += h[i];w } w}wint main()w{w int n;w cin >> n;w for (int i = 1; i <= n; i++)w h[i] = -1; //h数组初始化为-1w dfs(n); //由顶到下记忆化递归求解w cout << h[n]; w return 0;w}2021/8/2222【方法三】【方法三】 用递推,用h(n)表示自然数n所能扩展的数据个数,则h(1)=1, h(2)=2, h(3)=2, h(4)=4, h(5)=4, h(6)=6, h(7)=6, h(8)=10, h(9)=10.分析以上数据,可得递推公式:h(i)=1+h(1)+h(2)+…+h(i/2)。
此算法的时间度为O(n*n)w设h[i]-i按照规则扩展出的自然数个数(1≤i≤n)下表列出了h[i]值及其方案:2021/8/2223w【参考程序】【参考程序】w#includewusing namespace std;wint h[10001];wint main()w{w int n;w cin >> n;w for (int i = 1; i <= n; i++) //按照递增顺序计算扩展出的自然数的个数w {w h[i] = 1; //扩展出的自然数包括i本身w for (int j = 1; j <= i/2; j++) w //i左边分别加上1…自然数 按规则扩展出的自然数w h[i] += h[j]; w }w cout << h[n];w return 0;w}2021/8/2224【【方法四方法四】】w是对方法三的改进,我们定义数组s,s(x)=h(1)+h(2)+…+h(x),h(x)=s(x)-s(x-1),此算法的时间复杂度可降到O(n)。
w【【参考程序参考程序】】w#includewusing namespace std;wint h[1001],s[1001];wint main()w{w int n;w cin >> n;w for (int i = 1; i <= n; i++)w {w h[i] = 1 + s[i/2];w s[i] = s[i-1] + h[i]; //s是h的前缀累加和w }w cout << h[n];w return 0;w}2021/8/2225【【方法五方法五】】w 还是用递推,只要作仔细分析,其实我们还可以得到以下的递推公式: (1)当i为奇数时,h(i)=h(i-1);w (2)当i为偶数时,h(i)=h(i-1)+h(i/2).w【【参考程序参考程序】】w#includewusing namespace std;wint h[1001];wint main()w{w int n;w cin >> n;w h[1] = 1;w for (int i = 2; i <= n; i++)w {w h[i] = h[i-1];w if (i % 2 == 0) h[i] = h[i-1] + h[i/2];w }w cout << h[n];w return 0;w}2021/8/2226【【课堂练习课堂练习】】1、输入一串以‘!’结束的字符,按逆序输出。
用递归做)2、背包问题 问题:假设有n件质量分配为w1,w2,...,wn的物品和一个最多能装载总质量为T的背包,能否从这n件物品中选择若干件物品装入背包,使得被选物品的总质量恰好等于背包所能装载的最大质量,即wi1+wi2+...+wik=T若能,则背包问题有解,否则无解例如:有5件可选物品,质量分别为8千克、4千克、3千克、5千克、1千克假设背包的最大转载质量是10千克)3、阿克曼(Ackmann)函数A(x,y)中,x,y定义域是非负整数,函数值定义为:写出计算Ack(m,n)的递归算法程序 2021/8/22274、某人写了n封信和n个信封,如果所有的信都装错了信封求所有的信都装错信封共有多少种不同情况 基本形式:D[1]=0;d[2]=1递归式:d[n]= (n-1)*( d[n-1] + d[n-2]) 5、有52张牌,使它们全部正面朝上,从第2张开始,凡是2的倍数位置上的牌翻成正面朝下;接着从第3张牌开始,凡是3的倍数位置上的牌,正面朝上的翻成正面朝下,正面朝下的翻成正面朝上;接着第三轮从第4张牌开始,凡是4的倍数位置上的牌按上面相同规则翻转,以此类推,直到第1张要翻的牌是第52张为止。
统计最后有几张牌正面朝上,以及它们的位置号6、猴子吃桃问题猴子第一天摘下若干桃子,当即吃了一半,还不过瘾,又多吃了一个第二天早上又将剩下的桃子吃掉的一半,又多吃了一个以后每天早上都吃掉了前一天剩下的一半零一个到第10天早上想再吃时,见只剩下一个桃子了求第一天共摘多少桃子答案:1534)2021/8/2228【【上机练习上机练习】】1、斐波那切数列、斐波那切数列【问题描述】【问题描述】斐波那切数列0,1,1,2,3,5,8,13,21,34,55……从第三项起,每一项都是紧挨着的前两项的和写出计算斐波那切数列的任意一个数据项递归程序输入格式】【输入格式】输入所求的项数输出格式】【输出格式】 输出数据项的值输入样例】【输入样例】fbi.in 10【输出样例】【输出样例】fbi.out 342、倒序数、倒序数【问题描述】【问题描述】用递归算法写程序,输入一个非负整数,输出这个数的倒序数输入格式】【输入格式】输入一个非负整数输出格式】【输出格式】输出倒序结果输入样例】【输入样例】num.in 123【输出样例】【输出样例】num.out 3212021/8/22293、十进制转换成八进制、十进制转换成八进制【问题描述】【问题描述】用递归算法,把任一给定的十进制正整数转换成八进制数输出。
输入格式】【输入格式】输入一个正整数,表示需要转换的十进制数输出格式】【输出格式】输出一个正整数,表示转换之后的八进制的数输入样例】【输入样例】change.in15【输出样例】【输出样例】change.out174、求、求N!的值!的值【问题描述】【问题描述】 用递归算法,求N!的精确值(N以一般整数输入)输入样例】【输入样例】ni.in 10【输出样例】【输出样例】ni.out 10!=36288002021/8/22305、求最大公约数、求最大公约数【问题描述】 用递归方法求两个数m和n的最大公约数m>0(m>0,,n>0) n>0) 【输入格式】 输入二个数,即m和n的值输出格式】 输出最大公约数输入样例】 8 6【输出样例】 gcd=22021/8/22316、双色、双色Hanoi塔问题塔问题【问题描述】【问题描述】 设A、B、C是3 个塔座开始时,在塔座A 上有一叠共n 个圆盘,这些圆盘自下而上,由大到小地叠在一起各圆盘从小到大编号为1,2,……,n,奇数号圆盘着蓝色,偶数号圆盘着红色,如图所示现要求将塔座A 上的这一叠圆盘移到塔座B 上,并仍按同样顺序叠置。
在移动圆盘时应遵守以下移动规则: 规则(1):每次只能移动1 个圆盘; 规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上; 规则(3):任何时刻都不允许将同色圆盘叠在一起; 规则(4):在满足移动规则(1)-(3)的前提下,可将圆盘移至A,B,C 中任一塔座上 试设计一个算法,用最少的移动次数将塔座A 上的n个圆盘移到塔座B 上,并仍按同样顺序叠置2021/8/2232【编程任务】【编程任务】 对于给定的正整数n,编程计算最优移动方案输入格式】【输入格式】 由文件hanoi.in给出输入数据第1 行是给定的正整数n输出格式】【输出格式】 将计算出的最优移动方案输出到文件hanoi.out文件的每一行由一个正整数k和2个字符c1和c2组成,表示将第k个圆盘从塔座c1移到塔座c2上输入样例】【输入样例】3 【输出样例】【输出样例】1 A B2 A C1 B C3 A B1 C A2 C B1 A B2021/8/22337、背包问题、背包问题【问题描述】【问题描述】 简单的背包问题。
设有一个背包,可以放入的重量为s现有n件物品,重量分别为w1,w2…,wn,(1≤i≤n)均为正整数,从n件物品中挑选若干件,使得放入背包的重量之和正好为s找到一组解即可输入格式】【输入格式】 第一行是物品总件数和背包的载重量,第二行为各物品的重量输出格式】【输出格式】 各所选物品重量输入样例】【输入样例】5 101 2 3 4 5【输出样例】【输出样例】number:1 weight:1number:4 weight:4number:5 weight:52021/8/22348、、2的幂次方(的幂次方(Noip1998))【问题描述】【问题描述】 任何一个正整数都可以用2的幂次方表示例如: 137=27+23+20 同时约定方次用括号来表示,即ab 可表示为a(b) 由此可知,137可表示为: 2(7)+2(3)+2(0) 进一步:7= 22+2+20 (21用2表示) 3=2+20 所以最后137可表示为: 2(2(2)+2+2(0))+2(2+2(0))+2(0) 又如: 1315=210 +28 +25 +2+1 所以1315最后可表示为: 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)【输入格式】【输入格式】 正整数(n≤20000)【输出格式】【输出格式】 符合约定的n的0,2表示(在表示中不能有空格)【输入样例】【输入样例】 137【输出样例】【输出样例】2(2(2)+2+2(0))+2(2+2(0))+2(0)2021/8/22359、数的计数(、数的计数(Noip2001))【问题描述】【问题描述】 我们要求找出具有下列性质数的个数(包含输入的自然数n): 先输入一个自然数n(n≤1000), 然后对此自然数按照如下方法进行处理: 1.不作任何处理; 2.在它的左边加上一个自然数,但该自然数不能超过原数(输入的n)的一半; 3.加上数后,继续按此规则进行处理,直到不能再加自然数为止。
输入样例】【输入样例】 6 满足条件的数为 6 (此部分不必输出) 16 26 126 36 136【输出样例】【输出样例】62021/8/2236【编程任务】【编程任务】 给定正整数n 和m,计算出n 个元素的集合{1,2,…, n }可以划分为多少个不同的由m 个非空子集组成的集合输入格式】【输入格式】 由文件stir.in提供输入数据文件的第1 行是元素个数n和非空子集数m输出格式】【输出格式】 程序运行结束时,将计算出的不同的由m个非空子集组成的集合数输出到文件stir.out中输入样例】【输入样例】4 3 【输出样例】【输出样例】6【算法分析】【算法分析】 所求的是第2类Stirling数,通过可递推出如下递归式: S(n,m)=mS(n-1,m)+S(n-1,m-1); S(n,n+1)=0,S(n,0)=0,S(0,0)=12021/8/223710、集合划分问题、集合划分问题【问题描述】【问题描述】 n个元素的集合{1,2,…, n }可以划分为若干个非空子集。
例如,当n=4 时,集合{1,2,3,4}可以划分为15 个不同的非空子集如下:{{1},{2},{3},{4}}, {{1,2},{3},{4}}, {{1,3},{2},{4}}, {{1,4},{2},{3}}, {{2,3},{1},{4}},{{2,4},{1},{3}}, {{3,4},{1},{2}}, {{1,2},{3,4}}, {{1,3},{2,4}}, {{1,4},{2,3}},{{1,2,3},{4}}, {{1,2,4},{3}}, {{1,3,4},{2}}, {{2,3,4},{1}}, {{1,2,3,4}} 其中,集合{{1,2,3,4}}由1 个子集组成;集合{{1,2},{3,4}},{{1,3},{2,4}},{{1,4},{2,3}},{{1,2,3},{4}},{{1,2,4},{3}},{{1,3,4},{2}},{{2,3,4},{1}}由2 个子集组成;集合{{1,2},{3},{4}},{{1,3},{2},{4}},{{1,4},{2},{3}},{{2,3},{1},{4}},{{2,4},{1},{3}},{{3,4},{1},{2}}由3 个子集组成;集合{{1},{2},{3},{4}}由4 个子集组成。
2021/8/2238。