《算法设计与分析》实验报告
实验名称 : 实验1 递归与分治 1
人工智能与信息技术学院
南京中医药大学
实验目的:
(1) 熟悉递归与分治的概念
(2) 掌握递归与分治的使用
实验内容和要求
任务一:用递归方法解决以下问题。
X星球特别讲究秩序,所有道路都是单行线。一个甲壳虫车队,共16辆车,按照编号先后发车,夹在其它车流中,缓缓前行。
路边有个死胡同,只能容一辆车通过,是临时的检查站,如图所示。
X星球太死板,要求每辆路过的车必须进入检查站,也可能不检查就放行,也可能仔细检查。
如果车辆进入检查站和离开的次序可以任意交错。那么,该车队再次上路后,可能的次序有多少种?
为了方便起见,假设检查站可容纳任意数量的汽车。
显然,如果车队只有1辆车,可能次序1种;2辆车可能次序2种;3辆车可能次序5种。
现在足足有16辆车啊,亲!需要你计算出可能次序的数目。
这是一个整数,请输出答案,不要输出任何多余的内容(比如说明性文字)。
输入
没有输入。
输出
输出一个整数,即可能次序的数目。
任务二:实现数据结构中的快速排序,并用快排解决以下问题:
在一个划分成网格的操场上,n个士兵散乱地站在网格上,网格点由整数坐标(x,y)表示,士兵们可以沿网格边上、下、左、右移动,移动一格算一步。按照军官的命令,士兵们要整齐的列成一格水平队列,即排成(x,y),(x+1,y),....(x+n-1,y)。
问题是:如何选择x和y的值使得士兵们以最少的移动步数排成一列。
样例输入输出:
请输入士兵数目:
5
请输入5个士兵的坐标位置:
1 2
2 2
1 3
3 -2
3 3
使士兵排成一行所要移动的最少步数为:
8
思路:往中间靠拢才会总移动步数最少!将X轴和Y轴分开来看
1、对于Y方向
设n个士兵的排序之后的Y轴坐标分别为:Y1,Y2 …… …… Yn, 他们最后站在同一行上,设目标坐标为Y0,
则n个士兵最终在Y轴的需要移动的总的步数值为S1:
S1=|Y1-Y0|+|Y2-Y0|+ …… …… +|Yn-Y0|
结论:Y0取所有Yi的中间值时可以使得S1达到最小(这个结论可以证明)
2、对于X方向
(1)首先需要对所有士兵的X轴坐标值进行排序(为了方便就近移动)
(2)然后,按从左至右的顺序依次求出每个士兵所对应的“最终位置”(最优),所移动的步数总和就是X轴方向上需要移动的步数
设排序后n个士兵在X轴坐标为: X1,X2 ,X3 …… …… Xn
他们最终位置”的X轴坐标值为:X0,X0+1,X0+2 …… …… X0+(n-1)
则n个士兵最终X轴的需要移动的总的步数值为S2:S2=|X1-X0| + |X2-(X0+1)|+… +|Xn-(X0+n-1)|
经过变换S2=|X1-X0|+ |(X2-1)-X0|+ …+|(Xn-(n-1))-X0|
注意到公式的形式与Y轴方向上的考虑一样,同样是n个已知数分别减去一个待定数后取绝对值,然后求和
结论:求出x1, x2-1,… Xn-(n-1) 的中位数,即求得X0值,最后算出最优解。
思考:
1、如果n为偶数,中位数如何求?中间两个加起来除2
2、找中位数是否需要对整个数组排序?是否可以改进算法?
为了方便就近移动
任务三:给定 a, 分治法设计出求 a^n 的算法
算法设计思想
任务一:递归,栈,在检查地方有车分为进站和出站两种没有车只有入站直到所有车全部进站
任务二:注意到公式的形式与Y轴方向上的考虑一样,同样是n个已知数分别减去一个待定数后取绝对值,然后求和
结论:求出x1, x2-1,… Xn-(n-1) 的中位数,即求得X0值,最后算出最优解。
任务三:分治,分成偶数和奇数然后使用递归
程序设计(给出代码,加上必要的注释)
#include <iostream>
using namespace std;
int Car(int n, int m)
{
if (n == 0)
return 1;
if (m == 0)
return Car(n - 1, 1);
if (m > 0)
return Car(n - 1, m + 1) + Car(n, m - 1);
return 0;
}
int main()
{
cout<<Car(16,0);
return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxa = 10000;
struct node
{
int x, y;
}e[maxa];
int n;
double num= 0;
int cmpy(node a, node b)
{
return a.y < b.y;
}
int cmpx(node a, node b)
{
return a.x < b.x;
}
int main()
{
cout << "请输入士兵的人数:" << endl;
cin >> n;
cout << "请输入士兵的坐标(x,y),仅输入x和y的值即可" << endl;
for (int i = 1; i <= n; i++)
{
cin>> e[i].x>>e[i].y;
}
sort(e + 1, e + n + 1, cmpy);
int midy = e[(1 + n) / 2].y;
for (int i = 1; i <= n; i++)
{
num += abs(e[i].y - midy);
}
sort(e + 1, e + n + 1, cmpx);
for (int i = 1; i <= n; i++)
{
e[i].x = e[i].x - i;
}
sort(e + 1, e + n + 1, cmpx);
int midx = e[(1 + n) / 2].x;
for (int i = 1; i <= n; i++)
{
num += abs(e[i].x - midx);
}
cout <<"士兵移动的步数为"<< num << endl;
return 0;
}
#include<iostream>
using namespace std;
double f(int a, int n)
{
if (n == 1)
return a;
else
{
if (n % 2 == 0)
return f(a, n / 2) * f(a, n / 2);
else return f(a, n / 2) * f(a, n / 2 + 1);
}
}
int main()
{
int a, n;
cout << "请输入 a和n: ";
cin >> a >> n;
cout << a << "的" << n << "次方为:" << f(a, n)<< endl;
system("pause");
return 0;
}
运行结果(写清题号)
任务一
任务二
任务三
算法分析
递归: 递归是指一个函数自己调用自己。利用数学表达式可表达为:f(x)= f(x-1)+1递归算法一般要经历两个过程: 递推(归)和回溯
1、基准情形:必须要有某个基准情形,它无需递归就能解出2、不断推进:对于需要求解的情形,每次递归调用都必须要使状况朝向一种基本情况推进。3、设计法则:假设所有的递归调用都能运行。4、合成效益发展:在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作(例如使用递归计算斐波那契数列)
分治算法,就是把一个大的问题分为很多个形式相同的子问题,把问题规模缩小。假使,最初的问题规模是N,这些小的子问题的个数为a,子问题的规模是n / b,分解或者合并的复杂度表示为f( n ),那么总的时间复杂度就可以表示为
实验心得与小结