算法设计Word文档下载推荐.docx
《算法设计Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《算法设计Word文档下载推荐.docx(12页珍藏版)》请在冰豆网上搜索。
22
SampleOutput
CASE:
1
01
11
33448899
32248779
52061010711
55661101111
13131411181919
1312141418181719
1512121620171721
1515161620202121
实验二
众数问题
给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。
多重集S中重数最大的元素称为众数。
例如,S={1,2,2,2,3,5}。
多重集S的众数是2,其重数为3。
数据输入
输入包括多组数据,请处理到EOF结束。
每组数据,以一个n(1<
=100,000)开始,接下n行,每行有一个数字(-231~231)。
数据输出
对于每组输入数据,输出一行一个数字,表示众数。
如果存在多个解,只需输出值最小的众数即可。
算法思路:
首先用快速排序算法排序数组,找到当前数组的中位数及其位置,然后把在数组中与中位数相同的数字向它靠拢,就可以统计中位数的个数。
现在数组已经被中间这些相等的数字分开了。
那么递归条件就有了。
如果中位数的个数比它左边这段短,那么说明左边有可能找到更多的,所以递归继续。
反之,如果左边这段数的长度比较短,那么就没必要继续递归。
对右边那段数据使用同样的策略。
实验三
集合划分问题
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个子集组成。
给定正整数n和m,计算出n个元素的集合{1,2,...,n}可以划分为多少个不同的由m个非空子集组成的集合。
输入
输入的第1行是元素个数n和非空子集数m
输出
输出计算的不同的由m个非空子集组成的集合数
样例输入
43
样例输出
6
实验四:
整数因子分解问题
大于1的正整数n可以分解为:
n=x1*x2*…*xm。
例如,当n=12时,共有8种不同的分解式:
12=12;
12=6*2;
12=4*3;
12=3*4;
12=3*2*2;
12=2*6;
12=2*3*2;
12=2*2*3。
对于给定的正整数n,计算n共有多少种不同的分解式。
输入数据只有一行,有1个正整数n(1≤n≤2000000000)。
将计算出的不同的分解式数输出。
12
二、实验内容与思想设计
1、实验一的思想
核心算法思想:
分治思想在2kx2k个方格组成的棋盘中用到的L型骨牌恰有(4k-1)/3个。
为解决该问题的算法思想。
从分治的思想易得到需将问题微型化,于是我们将棋盘平均分成四个相似的棋盘。
若特殊方格恰在图示的第一块区域,为了符合分治的思想我们需要将其他三部分转化成同样具有特殊方格的棋盘,依次递归知道棋盘为1x1的规模即可解决问题。
我的理解:
倘若特殊方格在第一象限的区域内。
按照算法的思想将其余三块也转化成类似问题后一次递归,最终将会把2、3、4这三个格子留下来其余的格子会被全部覆盖掉。
而2、3、4着三个位置也恰好可以防止一个骨牌上去,最终就会完成整个问题的求解。
2、实验二的思想
3、实验三的思想
递归思想在n和k较小的时候,问题显得比较简单,但当n和k增大后,问题异常复杂。
此问题有一定的递归问题特点,用集合{1,2,3}划分为2个子集(n=3,k=2)来说明。
显然划分为一下2个子集有3中方法:
{{1,2},{3}}{{1,3},{2}}{{2,3},{1}}按照这个例子,我们可以将三个数字划分为2部分来看,前面的元素1和2为第一部分,第三个元素3为第二部分。
12|3如上所示,把12这部分先按照要求分为2个子集,在分别把3放入每一个子集一次,组成的就是2个划分。
还有一种可能就是3单独成为一个子集,那么12的部分分成2-1个子集组成的划分就可以了。
当n=3且k=2时,能够产生的划分总数是2*1+1=3种。
包含n个元素的集合进行k划分,假定总的划分次数为F(n,k),则其所有的划分可以由kF(n-1,k)和F(n-1,k-1)得到,前者是指将集合的前n-1个元素划分成k个子集,则将第k个元素可以加入其中的任一个子集形成一个新的划分,后者是指集合的前n-1个元素划分成k-1个子集,则第k个元素作为一个单独的元素组成一个子集形成k划分。
递归关系:
F(n,k)=k*F(n-1,k)+F(n-1,k-1)边界条件:
当k=1时,F(n,k)=1即分为一个子集,就是原集合,只有一种分发,包含所有元素。
当n=k时,F(n,k)=1即每个元素单独成为子集,只有一种分发,每个子集包含一个元素。
当n<
k时,F(n,k)=0即2个元素要分成3个子集,又不允许出现空集,那么这是不可能实现的,划分个数为0。
4、实验四的思想
运用递归思想可以设计程序。
窗体顶端
三、测试结果
1、实验一测试结果如下图:
结果一:
结果二:
2、实验二的测试结果
3、实验三的测试结果
4、实验四的测试结果
四、实验心得
算法总体思想:
对这k个子问题分别求解。
如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求出其解为止。
将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解。
直接或间接地调用自身的算法称为递归算法。
用函数自身给出定义的函数称为递归函数。
由分治法产生的子问题往往是原问题的较小模式,这就为使用递归技术提供了方便。
在这种情况下,反复应用分治手段,可以使子问题与原问题类型一致而其规模却不断缩小,最终使子问题缩小到很容易直接求出其解。
这自然导致递归过程的产生。
分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
通过以上返利学习分治策略设计技巧,熟悉了分治递归思想,进一步掌握递归算法的设计思想以及递归程序的调试技术。
在做实验的过程中理解这样一个观点:
分治与递归经常同时应用在算法设计之中。
五、附:
1、实验一棋盘覆盖问题程序
#include<
iostream>
iomanip>
usingnamespacestd;
intboard[100][100];
//存放棋盘L型的标号数组;
inttile=0;
//L型骨牌号
intmain()
{
voidchessBoard(inttr,inttc,intdr,intdc,intsize);
//棋盘的覆盖方法;
voiddisplay(int*board,intsize);
//棋盘覆盖图;
intsize,dr,dc;
cout<
<
"
\t棋盘覆盖问题\n"
;
2^k×
2^k个方格变长size(size=2,4,8,16,32,64):
cin>
>
size;
分别输入特殊块的行列下标dr,dc(0-"
(size-1)<
):
dr>
dc;
board[dr][dc]=-1;
棋盘覆盖图:
\n"
chessBoard(0,0,dr,dc,size);
inti,j;
for(i=0;
i<
i++)
{
for(j=0;
j<
j++)
cout<
setw(6)<
board[i][j];
cout<
endl<
endl;
}
return0;
}
voidchessBoard(inttr,inttc,intdr,intdc,intsize)
if(size==1)
return;
intt=tile++;
ints=size/2;
//分割棋盘
//覆盖左上角子棋盘
if(dr<
tr+s&
&
dc<
tc+s)//特殊方格在此棋盘中
chessBoard(tr,tc,dr,dc,s);
else//此棋盘中无特殊方格
board[tr+s-1][tc+s-1]=t;
//用t号L型骨牌覆盖右下角
chessBoard(tr,tc,tr+s-1,tc+s-1,s);
//覆盖其余方格
//覆盖右上角子棋盘
if(dr<
tr+s&
dc>
=tc+s)//特殊方格在此棋盘中
chessBoard(tr,tc+s,dr,dc,s);
board[tr+s-1][tc+s]=t;
//用t号L型骨牌覆盖左下角
chessBoard(tr,tc+s,tr+s-1,tc+s,s);
//覆盖左下角子棋盘
if(dr>
=tr+s&
dc<
tc+s)//特殊方格在此棋盘中
chessBoard(tr+s,tc,dr,dc,s);
board[tr+s][tc+s-1]=t;
//用t号L型骨牌覆盖右上角
chessBoard(tr+s,tc,tr+s,tc+s-1,s);
//覆盖右下角子棋盘
chessBoard(tr+s,tc+s,dr,dc,s);
else
board[tr+s][tc+s]=t;
//用t号L型骨牌覆盖左上角
chessBoard(tr+s,tc+s,tr+s,tc+s,s);
//覆盖其余方格
2、实验二众数问题程序
map>
usingstd:
:
cout;
cin;
map;
//定义一个map类型,键为数,值为其重复次数
map<
int,int>
s_map;
typedefmap<
const_iteratorIterator;
//计算的函数,参数为一个数组和其长度
Iteratorcalc(int*list,intlen)
for(inti=0;
i<
len;
++i)
++s_map[list[i]];
//找出众数,即出现次数最多的数
Iteratortimes=s_map.begin();
Iteratoritr;
for(itr=s_map.begin();
itr!
=s_map.end();
++itr)
if(times->
second<
itr->
second)
times=itr;
returntimes;
intsize;
cin>
size;
//输入多重集S元素的个数
int*ps=newint[size];
ps[i];
Iteratoritr=calc(ps,size);
cout<
first<
endl//众数
<
endl;
//其重数
3、实验三集合划分问题程序
#include"
stdio.h"
intF(intx,inty)
if(y==1)
return
(1);
elseif(x==y)
return(F(x-1,y-1)+y*F(x-1,y));
intsum=0;
intF(intx,inty);
printf("
请输入集合的元数:
);
scanf("
%d"
&
i);
for(j=1;
=i;
sum=sum+F(i,j);
%d元集合的划分总数是:
%d\n"
i,sum);
4、实验四整数因子分解问题
intnum=0;
//计数器
//递归求数的分解的个数
voidsolve(longn){
if(1==n){
num++;
}elseif(1<
n){
for(inti=n;
i>
=2;
i--){
doubleresult=n*1.0/i;
if(result==(long)result){
solve(n/i);
intmain(){
longn;
std:
cin>
n;
solve(n);
cout<
num<
'
\n'
;