算法设计与分析报告报告材料实验二Word文件下载.docx
《算法设计与分析报告报告材料实验二Word文件下载.docx》由会员分享,可在线阅读,更多相关《算法设计与分析报告报告材料实验二Word文件下载.docx(14页珍藏版)》请在冰豆网上搜索。
2.
thenreturn(ADHOC(P))
3.
将P分解为较小的子问题P1,P2,...,Pk
4.
fori←1tok
5.
doyi←Divide-and-Conquer(Pi)
△递归解决Pi
6.
T←MERGE(y1,y2,...,yk)
△合并子问题
7.
return(T)
其中|P|表示问题P的规模;
n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。
ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。
因此,当P的规模不超过n0时,直接用算法ADHOC(P)求解。
算法MERGE(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问题P1,P2,...,Pk的相应的解y1,y2,...,yk合并为P的解。
根据分治法的分割原则,原问题应该分为多少个子问题才较适宜?
各个子问题的规模应该怎样才为适当?
这些问题很难予以肯定的回答。
但人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。
换句话说,将一个问题分成大小相等的k个子问题的处理方法是行之有效的。
许多问题可以取k=2。
这种使子问题规模大致相等的做法是出自一种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。
分治法的合并步骤是算法的关键所在。
有些问题的合并方法比较明显,有些问题合并方法比较复杂,或者是有多种合并方案;
或者是合并方案不明显。
究竟应该怎样合并,没有统一的模式,需要具体问题具体分析。
四、实验内容
1、编程实现归并排序算法和快速排序算法,程序中加入比较次数的计数功能,输出排序结果和比较次数。
输入10组相同的数据,验证排序结果和完成排序的比较次数。
用表格列出比较结果。
给出文字分析。
2、汉诺塔(hanoi)问题。
3、棋盘覆盖问题。
4、循环赛日程安排问题。
五、算法设计
1、归并排序算法
procedureMERGESORT(low,high)
//A(low;
high)是一个全程数组,它含
有high-low+1≥0个待排序的元素//
integerlow,high;
iflow<
high;
thenmid←,//求这个集合的分割点//
callMERGESORT(low,mid)//将一个子集合排序//
callMERGESORT(mid+1,high)//将另一个子集合排序
callMERGE(low,mid,high)//归并两个已排序的子集合//
endif
endMERGESORT
归并两个已排序的集合
procedureMERGE(low,mid,high)
//A(low:
high)是一个全程数组//
//辅助数组B(low;
high)//
integerh,i,j,k;
h←low;
i←low;
j←mid+1;
whileh≤midandj≤highdo//当两个集合都没取尽时//
ifA(h)≤A(j)thenB(i)←A(h);
h←h+1
elseB(i)←A(j);
j←j+1
i←i+1
repeat
ifh>
midthen
fork←jtohighdo//处理剩余的元素//
B(i)←A(k);
i←i+1
elsefork←htomiddo
将已归并的集合复制到A
endMERGE
2、快速排序算法
我们已经知道,在决策树计算模型下,任何一个基于比较来确定两个元素相对位置的排序算法需要Ω(nlogn)计算时间。
如果我们能设计一个需要O(n1ogn)时间的排序算法,则在渐近的意义上,这个排序算法就是最优的。
许多排序算法都是追求这个目标。
下面介绍快速排序算法,它在平均情况下需要O(nlogn)时间。
这个算法是由C.A.R.Hoare发明的。
算法的基本思想:
快速排序的基本思想是基于分治策略的。
对于输入的子序列L[p..r],如果规模足够小则直接进行排序,否则分三步处理:
分解(Divide):
将输入的序列L[p..r]划分成两个非空子序列L[p..q]和L[q+1..r],使L[p..q]中任一元素的值不大于L[q+1..r]中任一元素的值。
递归求解(Conquer):
通过递归调用快速排序算法分别对L[p..q]和L[q+1..r]进行排序。
合并(Merge):
由于对分解出的两个子序列的排序是就地进行的,所以在L[p..q]和L[q+1..r]都排好序后不需要执行任何计算L[p..r]就已排好序。
这个解决流程是符合分治法的基本步骤的。
因此,快速排序法是分治法的经典应用实例之一。
QuickSort(p,q)
//将数组A[1:
n]中的元素
A[p],A[p+1],,A[q]按不降次序排列,
并假定A[n+1]是一个确定的、且大于
A[1:
n]中所有的数。
//
intp,q;
globaln,A[1:
n];
ifp<
qthen
j=Partition(p,q+1);
//划分后j成为划分元素的位置
QuickSort(p,j-1);
QuickSort(j+1,q);
endQuickSort
procedurePARTITION(m,p)
//退出过程时,p带着划分元素所在的下标位置。
integerm,p,i;
globalA(m:
p-1)
v←A(m);
i←m//A(m)是划分元素//
loop
loopi←i+1untilA(i)≥vrepeat//i由左向右移//
loopp←p-1untilA(p)≤vrepeat//p由右向左移//
ifi<
p
thencallINTERCHANGE(A(i),A(p))//A(i)和A(p)换位//
elseexit
A(m)←A(p);
A(p)←v//划分元素在位置p//
EndPARTITION
3、汉诺塔(hanoi)问题。
设有A、B、C共3根塔座,在塔座A上堆叠n个金盘,每个盘大小不同,只允许小盘在大盘之上,最底层的盘最大,如下图所示。
现在要求将A上的盘全都移到C上,在移的过程中要遵循以下原则:
每次只能移动一个盘;
圆盘可以插在A、B和C任一个塔座上;
在任何时刻,大盘不能放在小盘的上面。
hanoi问题递归求解思想:
我们把一个规模为n的hanoi问题:
1到n号盘按照移动规则从A上借助B移到C上表示为H(A,B,C,n);
原问题划分成如下三个子问题:
(1)将1到n-1号盘按照移动规则从A上借助C移到B上H(A,C,B,n-1);
(2)将n号盘从A上直接移到C上;
(3)将1到n-1号盘按照移动规则从B上借助A移到C上H(B,A,C,n-1);
经过三个子问题求解,原问题的也即求解完成。
4、盘覆盖问题。
在一个2k×
2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
六、参考程序代码
1、归并排序
#include<
iostream.h>
iomanip.h>
stdlib.h>
time.h>
#defineM11
typedefintKeyType;
typedefintElemType;
structrec{
KeyTypekey;
ElemTypedata;
};
typedefrecsqlist[M];
classguibing{
public:
guibing(sqlistb)
{
for(inti=0;
i<
M;
i++)
r[i]=b[i];
}
voidoutput(sqlistr,intn)
{
for(inti=0;
n;
cout<
<
setw(4)<
r[i].key;
cout<
endl;
}
voidxuanze(sqlistb,intm,intn)
inti,j,k;
for(i=m;
n-1;
{
k=i;
for(j=i;
j<
j++)
if(b[k].key>
b[j].key)k=j;
if(k!
=i)
{
rectemp=b[k];
b[k]=b[i];
b[i]=temp;
}
}
voidmerge(intl,intm,inth,sqlistr2)
xuanze(r,l,m);
xuanze(r,m,h);
output(r,M);
k=i=l;
for(j=m;
m&
&
h;
k++)
if(r[i].key<
=r[j].key)
{
r2[k]=r[i];
i++;
}
else
r2[k]=r[j];
j++;
output(r2,M);
while(j<
h)
r2[k]=r[j];
j++;
k++;
while(i<
=m)
r2[k]=r[i];
i++;
output(r2,M);
private:
sqlistr;
};
voidmain()
cout<
"
guibingfa1运行结果:
\n"
;
sqlista,b;
inti,j=0,k=M/2,n=M;
srand(time(0));
for(i=0;
a[i].key=rand()%80;
b[i].key=0;
guibinggx(a);
排序前数组:
gx.output(a,M);
数组排序过程演示:
gx.merge(j,k,n,b);
排序后数组:
gx.output(b,M);
cin.get();
2、快速排序
#defineMAXI10
typedefrecsqlist[MAXI];
classkuaisu
{
kuaisu(sqlista,intm):
n(m)
i++)b[i]=a[i];
voidquicksort(ints,intt)
inti;
if(s<
t){
i=part(s,t);
quicksort(s,i-1);
quicksort(i+1,t);
elsereturn;
intpart(ints,intt)
inti,j;
recp;
i=s;
j=t;
p=b[s];
while(i<
j)
j&
b[j].key>
=p.key)j--;
b[i]=b[j];
b[i].key<
=p.key)i++;
b[j]=b[i];
b[i]=p;
output();
returni;
voidoutput()
b[i].key;
sqlistb;
intn;
voidmain()
cout<
kuaisu1.cpp运行结果:
sqlista1;
inti,n=MAXI,low=0,high=9;
srand(time(0));
for(i=0;
a1[i].key=rand()%80;
kuaisupx(a1,n);
px.quicksort(low,high);
px.output();
cin.get();
}
3、hanoi问题递归求解代码:
voidH(charA,charB,charC,intn)
if(n>
0)
H(A,C,B,n-1);
printf(“%dfrom%cto%c”,n,A,C);
H(B,A,C,n-1);
4、棋盘覆盖问题。
voidchessBoard(inttr,inttc,intdr,intdc,intsize)
if(size==1)return;
intt=tile++,
//L型骨牌号
s=size/2;
//分割棋盘
//覆盖左上角子棋盘
if(dr<
tr+s&
dc<
tc+s)
//特殊方格在此棋盘中
chessBoard(tr,tc,dr,dc,s);
else{//此棋盘中无特殊方格
//用t号L型骨牌覆盖右下角
board[tr+s-1][tc+s-1]=t;
//覆盖其余方格
chessBoard(tr,tc,tr+s-1,tc+s-1,s);
//覆盖右上角子棋盘
dc>
=tc+s)
chessBoard(tr,tc+s,dr,dc,s);
//用t号L型骨牌覆盖左下角
board[tr+s-1][tc+s]=t;
chessBoard(tr,tc+s,tr+s-1,tc+s,s);
//覆盖左下角子棋盘
if(dr>
=tr+s&
chessBoard(tr+s,tc,dr,dc,s);
else{//用t号L型骨牌覆盖右上角
board[tr+s][tc+s-1]=t;
chessBoard(tr+s,tc,tr+s,tc+s-1,s);
//覆盖右下角子棋盘
chessBoard(tr+s,tc+s,dr,dc,s);
else{//用t号L型骨牌覆盖左上角
board[tr+s][tc+s]=t;
chessBoard(tr+s,tc+s,tr+s,tc+s,s);
5、循环赛日程安排问题
#include"
stdio.h"
voidTable(intk,inta[][9])
intn=1;
for(inti=1;
=k;
i++)n*=2;
for(i=1;
=n;
i++)a[1][i]=i;
intm=1;
for(ints=1;
s<
s++)
n/=2;
for(intt=1;
t<
t++)
for(i=m+1;
=2*m;
for(intj=m+1;
a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m];
a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2];
m*=2;
main()
intk=3;
inta[9][9]={0};
Table(k,a);
=8;
{for(intj=1;
printf("
%3d"
a[i][j]);
printf("
);
思考问题:
1、递归的关键问题在哪里?
2、递归与非递归之间程序的转换?