算法设计与分析实验指导书Word文档下载推荐.docx
《算法设计与分析实验指导书Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《算法设计与分析实验指导书Word文档下载推荐.docx(38页珍藏版)》请在冰豆网上搜索。
S1={p∈S|p(x)≤m},S2={p∈S|p(x)>
m}
其中:
m是S中各点x坐标的中位数,因此S1和S2中点数大致相同,且S=S1∪S2。
递归地在S1和S2上解最接近点对的问题,分别得到S1和S2中的最小距离d1和d2,并设d=min{d1,d2}。
用P1和P2分别表示在直线L左边和右边与其距离在d范围的点构成的两个垂直长条平面区域。
P1:
{p∈P1|(|m-x(p)|≤d)},P2:
{p∈P2|(|x(p)-m|≤d)}
S中的最接近点对的距离或者是d,或者是某个{p,q}点对的距离,其中p∈P1,q∈P2。
如果{p,q}是S中的最接近点对,则必有distance(p,q) 将上述描述简化如下:
(1)递归用L将节点集分成两个相等的部分;
(2)设d是d1和d2的最小值;
(3)删除离L超过d的节点;
(4)按照y维扫描剩下的节点,计算5个邻居的距离;
(5)如果距离小于d,更新d
下面我们分析分治算法的时间复杂度,由于步骤2-5是每个递归过程中都要去做的,我们先分析2-5。
步骤2时间复杂度为O
(1);
步骤3时间复杂度为O(n);
步骤4时间复杂度为O
(1);
步骤5时间复杂度为O
(1),所以步骤2-5的时间复杂度为O(n)。
步骤1递归地将平面S中的点分成两个相等的部分时间复杂度为O(logn),所以总的时间复杂度为O(n)*O(logn)=O(nlogn)。
(2)参考代码
#include<
iostream>
#include<
cmath>
cstdio>
cstdlib>
cstring>
usingnamespacestd;
constintN=100005;
constdoubleMAX=10e100;
constdoubleeps=0.00001;
typedefstructTYPE
{
doublex,y;
intindex;
}Point;
Pointa[N],b[N],c[N];
doubleclosest(Point*,Point*,Point*,int,int);
doubledis(Point,Point);
intcmp_x(constvoid*,constvoid*);
intcmp_y(constvoid*,constvoid*);
intmerge(Point*,Point*,int,int,int);
inlinedoublemin(double,double);
intmain()
intn,i;
doubled;
scanf("
%d"
&
n);
while(n)
{
for(i=0;
i<
n;
i++)
%lf%lf"
(a[i].x),&
(a[i].y));
qsort(a,n,sizeof(a[0]),cmp_x);
a[i].index=i;
memcpy(b,a,n*sizeof(a[0]));
qsort(b,n,sizeof(b[0]),cmp_y);
d=closest(a,b,c,0,n-1);
printf("
%.2lf\n"
d);
}
return0;
}
doubleclosest(Pointa[],Pointb[],Pointc[],intp,intq)
if(q-p==1)
returndis(a[p],a[q]);
if(q-p==2)
doublex1=dis(a[p],a[q]);
doublex2=dis(a[p+1],a[q]);
doublex3=dis(a[p],a[p+1]);
if(x1<
x2&
&
x1<
x3)
returnx1;
elseif(x2<
returnx2;
else
returnx3;
intm=(p+q)/2;
inti,j,k;
doubled1,d2;
for(i=p,j=p,k=m+1;
=q;
if(b[i].index<
=m)
c[j++]=b[i];
//数组c左半部保存划分后左部的点,且对y是有序的.
c[k++]=b[i];
d1=closest(a,c,b,p,m);
d2=closest(a,c,b,m+1,q);
doubledm=min(d1,d2);
merge(b,c,p,m,q);
//数组c左右部分分别是对y坐标有序的,将其合并到b.
for(i=p,k=p;
if(fabs(b[i].x-b[m].x)<
dm)
//找出离划分基准左右不超过dm的部分,且仍然对y坐标有序.
for(i=p;
k;
for(j=i+1;
j<
k&
c[j].y-c[i].y<
dm;
j++)
doubletemp=dis(c[i],c[j]);
if(temp<
dm=temp;
}returndm;
doubledis(Pointp,Pointq)
doublex1=p.x-q.x,y1=p.y-q.y;
returnsqrt(x1*x1+y1*y1);
intmerge(Pointp[],Pointq[],ints,intm,intt)
for(i=s,j=m+1,k=s;
=m&
=t;
)
if(q[i].y>
q[j].y)
p[k++]=q[j],j++;
p[k++]=q[i],i++;
while(i<
p[k++]=q[i++];
while(j<
=t)
p[k++]=q[j++];
memcpy(q+s,p+s,(t-s+1)*sizeof(p[0]));
intcmp_x(constvoid*p,constvoid*q)
doubletemp=((Point*)p)->
x-((Point*)q)->
x;
if(temp>
0)
return1;
elseif(fabs(temp)<
eps)
return-1;
intcmp_y(constvoid*p,constvoid*q)
y-((Point*)q)->
y;
inlinedoublemin(doublep,doubleq)
return(p>
q)?
(q):
(p);
实验三、快速排序算法
利用快速排序算法编制程序对给定的一组数排序,并分析其性能。
掌握分治法解决问题的策略;
学会使用分治法设计程序并能分析它。
快速排序的基本思想是基于分治策略的。
对于输入的子序列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]就已排好序。
QSort(Sqlist&
L,intlow,inthigh){
c=high-low;
/*循环次数*/
if(c<
10)直接调用插入排序法;
/*小数时直接调用插入排序法*/
if(L.r[low].key>
L.r[high].key)L.r[low]<
->
L.r[high];
/*确保区间内第一个元素的值不大于区间内最后一个元素的值*/
ilow=low;
/*小于区间内第一个元素的值数组边界下标*/
ihigh=high;
/*大于区间内最后一个元素的值数组边界下标*/
for(i=low+1;
i<
c;
i++){
if(L.r[i].key<
L.r[low].key)L.r[i]<
L.r[++ilow];
/*小于区间内第一个元素的值放置ilow区间内*/
else
if(L.r[i].key>
L.r[high].key){
L.r[i]<
L.r[--ihigh];
/*大于区间内最后一个元素的值放置ihigh区间内*/
i--;
/*下一个比较位置不变*/
c--;
/*循环次数减一*/
}
L.r[ilow]<
L.r[low];
/*将小于区间内第一个元素的边界下标元素与第一个元素互换*/
L.r[ihigh]<
/*将大于区间内最后一个元素的边界下标元素与最后一个元素互换*/
QSort(L,low,ilow-1);
QSort(L,ilow+1,ihigh-1);
QSort(L,ihigh+1,high);
voidQuickSort(Sqlist&
L)
{
QSort(L,1,L.length);
实验四、最大子段和(分治法)
编制程序求解如下问题:
给定由n个整数(可能有负整数)组成的序列(a1,a2,…,an),最大子段和问题要求该序列形如
的最大值(1<
=i<
=j<
=n),当序列中所有整数均为负整数时,其最大子段和为0。
进一步掌握递归算法的设计思想以及递归程序的调式技术;
理解这样一个观点,分治与递归经常同时应用在算法设计之中。
首先判断输入的数组是不是一个元素。
如果是,则再看该元素是不是大于0.如果大于0,则问题的解为该元素的值,否则为0;
如果输入的数组不是一个数,则求出其数组中间的元素下标。
递推对前半部分数组(low,center)和后半部分(center+1,high)求最大字段和.之后,设置一个左标记和一个右标记。
从数组中间向前边执行for语句。
在这个过程中,把当前元素加到左标记里面去。
同时判断左标记是不是大于左部分可能的字段和,如果大于,把左部分可能的字段和值设置为左标记的值。
对数组的右半部分做同样操作。
把右半部分的当前元素加到右标记里面去。
同时判断右标记是不是大于右部分可能的字段和,如果大于,把右部分可能的字段和值设置为右标记的值。
之后设置可能的问题解为左部分可能解和右部分可能解之和。
之后判断该可能解是不是大于该数组左边和右边的递推解。
如果前者没有后面2者的一个大。
则设置问题的最终解为最大者。
stdio.h>
#defineN10
intsubMaxSum(inta[],intlow,inthigh)/*quickSort*/
intsum=0;
inti;
if(low==high){
sum=a[low];
if(sum<
0)
sum=0;
else{
intcenter=(low+high)/2;
ints1=0;
intleftsum=0;
intrightsum=0;
ints2=0;
leftsum=subMaxSum(a,low,center);
rightsum=subMaxSum(a,center+1,high);
for(i=center;
i>
=low;
i--){
leftsum+=a[i];
if(leftsum>
s1)s1=leftsum;
for(i=center+1;
=high;
i++){
rightsum+=a[i];
if(rightsum>
s2)s2=leftsum;
sum=s1+s2;
leftsum)sum=leftsum;
rightsum)sum=rightsum;
returnsum;
intMaxSum(inta[],intn)
returnsubMaxSum(a,0,n-1);
voidmain(void){
inti,sum;
inttest[N];
for(i=0;
N;
pleaseinputthe%dnumber:
"
i+1);
&
test[i]);
\n"
);
yourinputis:
i++)
%d"
test[i]);
sum=MaxSum(test,N);
themaxsumis:
%d\n"
sum);
实验五、最长公共子序列问题
利用动态规划算法编制程序求解下面的问题:
若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:
zj=xij。
例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。
熟悉最长公共子序列问题的算法;
初步掌握动态规划算法;
最长公共子序列问题具有最优子结构性质。
设X={x1,...,xm},Y={y1,...,yn},及它们的最长子序列Z={z1,...,zk},则:
1、若xm=yn,则zk=xm=yn,且Z[k-1]是X[m-1]和Y[n-1]的最长公共子序列
2、若xm!
=yn,且zk!
=xm,则Z是X[m-1]和Y的最长公共子序列
3、若xm!
=yn,且zk!
=yn,则Z是Y[n-1]和X的最长公共子序列
由性质导出子问题的递归结构
当i=0,j=0时,c[i][j]=0
当i,j>
0;
xi=yi时,c[i][j]=c[i-1][j-1]+1
xi!
=yi时,c[i][j]=max{c[i][j-1],c[i-1][j]}
#include"
iostream.h"
iomanip.h"
#definemax100
voidLCSLength(intm,intn,char*x,char*y,char*b)
inti,j,k;
intc[max][max];
for(i=1;
=m;
i++)
c[i][0]=0;
=n;
c[0][i]=0;
for(j=1;
j++)
if(x[i-1]==y[j-1])
c[i][j]=c[i-1][j-1]+1;
k=i*(n+1)+j;
b[k]='
\\'
;
elseif(c[i-1][j]>
=c[i][j-1])
c[i][j]=c[i-1][j];
|'
else
c[i][j]=c[i][j-1];
-'
voidLCS(inti,intj,char*x,char*b,intwidth)
if(i==0||j==0)
return;
intk=i*(width+1)+j;
if(b[k]=='
)
LCS(i-1,j-1,x,b,width);
cout<
<
x[i]<
endl;
elseif(b[k]=='
LCS(i-1,j,x,b,width);
LCS(i,j-1,x,b,width);
voidmain()
{charx[max]={'
a'
'
b'
c'
d'
};
chary[max]={'
intm=7;
intn=6;
charb[max]={0};
LCSLength(m,n,x,y,b);
LCS(m,n,x,b,n);
endl<
实验六、最大子段和(动态规划)
运用动态规划法编制一个程序求解实验3的最大字段和问题,并对这两种算法比较分析。
深刻掌握动态规划法的设计思想并能熟悉运用;
理解这样一个观点,同样的问题可以用不同的方法解决,一个好的算法是返回努力和重新修正的结果。
若记b[j]=max(a[i]+a[i+1]+..+a[j]),其中1<
=j,并且1<
=n。
则所求的最大子段和为maxb[j],1<
由b[j]的定义可易知,当b[j-1]>
0时b[j]=b[j-1]+a[j],否则b[j]=a[j]。
故b[j]的动态规划递归式为:
b[j]=max(b[j-1]+a[j],a[j]),1<
据此,可设计出求最大子段和问题的动态规划算法。
intmaxSum(int[]a)
{intsum=0;
intb=0;
for(inti=0;
a.length;
{if(b>
0)b+=a[i];
elseb=a[i];
if(b>
sum)sum=b;
returnsum;
实验七、哈夫曼编码
运用贪心法编制程序求解如下问题:
设需要编码的字符集为{d1,d2,…dn},它们出现的频率为{w1,w2,…wn},应用哈夫曼树构造最短的不等长编码方案。
了解前缀