1、 试阐述回溯法和分支限界法在算法原理、搜索策略、剪枝、求解以及性能方面的异同:
回溯法 分支限界法
算法原理 都是搜索问题的解空间树找到问题的解
搜索策略 深度优先搜索(堆栈) 广度优先搜索(优先队列)
剪枝 按约束条件剪枝 按目标函数剪枝
求解 求出问题的所有可能解 求出特定意义下的最优解(使目标函数取极值的解)
性能 搜索的盲目性 目的性更强,能在更高层次上进行剪枝
(两种方法都可以看成对蛮力法的改进。其中回溯法对解空间树按照深度优先搜索策略遍历,在搜索过程中采用约束条件和目标函数实行剪枝;分支限界法按照广度优先的顺序搜索解空间树,在遍历过程中根据限界函数估算目标函数的可能值,从中选择使目标函数取极值的结点优先扩展。对于超出目标函数界的结点,则直接丢弃。因分支限界法搜索的目标性更强,且能在更高的层次上施行剪枝,因此一般情况下其时间性能较回溯法要好。)
2、阐述动态规划法的基本思想,它与分治法有哪些异同:
动态规划法将问题划分成若干个重叠子问题,子问题的重叠关系表现在对给定问题求解的递推关系,先对子问题进行求解中,将子问题的解求解一次并填入表中,当需要再次求解该子问题时,只需从表中取出对应值,从而避免大量的重叠运算。
动态规划法分为三步:
(1)划分子问题:
(2)确定动态规划函数:根据子问题间的重叠关系找到满足条件的递推关系式(动态规划函数)
(3)填写表格:自底向上的方式计算各子问题的解并填表,实现动态规划过程
同:都具有最优子结构,都是将待解问题分解成若干个子问题进行求解,先求解子问题,再通过子问题求出原问题。所以,两者的求解步骤其实很相似。
异:
动态规划法 分治法
重叠子问题 子问题相互独立
自底向上计算 自顶向下计算
3、贪心法设计思想:
贪心法把一个问题分继承一系列较为简单的局部最优选择,每一步选择都是对当前解的一个扩展,直到获得问题的完整解。并不是从整体最优来考虑的,局部最优选择。因此,并不能保证获得整体最优解,但通常能获得近似最优解。(自顶向下进行计算的)
用贪心法求解做优化问题所需性质:贪心选择性质,最优子结构
设计分治算法,求一个数组中第二大的数:
int Partition(int a[], int low, int high){
int i=low, j=high;
while( i<j ){
while( i<j&&a[i]>=a[j] )
j--;
if( i<j ){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
}
while( i<j&&a[i]>=a[j] )
i++;
if( i<j ){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
j--;
}
}
for(int i=0;i<=9;i++)
cout<<a[i]<<" ";
cout<<endl;
return i;
}
int SelectMink(int a[], int low, int high, int k){
int s;
s = Partition(a, low, high);
cout<<s<<"-->"<<a[s]<<endl;
if( s==k ){
return a[k];
}
if( s>k ){
return SelectMink(a, low, s+1, k);
}
if( s<k ){
return SelectMink(a, s-1, high, k);
}
}
减治法求解选择问题(求序列中第k大的数的索引,借助快速排序中的划分进行求解):
时间复杂度:
最好:O(n)
最坏:O(n2) 相当于冒泡
平均:O(n)
动态规划法例题
例题1多源点最短路径(Floyd算法):
d(vi, vj, {}) = cij
d(vi, vj, {v1,v2…vk}) = min{ d(vi, vj, {v1,v2…vk-1}), d(vi, vk, {v1,v2…vk-1})+d(vk, vj, {v1,v2…vk-1}) }
例题2最长递增子序列O(n^2):
L(i) = 1 i=1或不存在a[j]<a[i] 1=<j<i
L(i) = max( L(j)+1 ) 对所有a[j]<a[i]的j 1=<j<i
例题3 0/1背包:
V(i, j) = V(i-1, j) j<w[i] 无法装下物品i
V(i, j) = max{ V(i-1, j-w[i])+v[i], V(i-1, j) } j>=w[i]
(可以装的下,但到底装不装要比较哪种好)
最小生成树两种算法
prim算法:
随机挑选一个节点作为新图的初始状态,开始选择节点,加入新图,每次选择最小边且不产生回路,直到所有的点都挑进来了。
kruskal算法:
设一个图只有节点,没有边,不断加入最小的边,不产生回路,直到变为连通。
贪心法 活动安排问题:
时间复杂度O(nlogn) 主要用在排序上
算法:
- 按 活动结束时间 升序排列
- 最优解一定包含第一个活动,即ans = {1}
- j = 集合B中最晚结束的活动, 初始化为j=1
- 循环变量i从 2-n 依次考察每个活动
4.1 若s[i] > f[j]:
B = B+{i};
j = i;
4.2 i++;
渐进符号
O(n) 渐进上界 最差也就这么差 o(n) T(n)<O( f(n) 没有等号
Ω(n) 渐进下界 最好最快是这种情况
θ(n) c1f(n)<=T(n)<=c2f(n)