算法设计与分析 复习总结


参考资料:算法设计与分析_北航_中国大学MOOC


分治算法

概要

在这里插入图片描述

最大子数组问题1

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
伪代码:

MaxSubArray(X, low, high)
输入:数组X, 数组下标 low, high
输出:最大子数组S_max
if low = high then                             //递归终止条件
    return X[low]
end
else
	mid <- ⌊(low+high)/2⌋                      //拆分原问题
	S1 <- MaxSubArray(X, low, mid)            //求解子问题, T(n/2)
	S2 <- MaxSubArray(X, mid+1, high)
	S3 <- CrossingSubArray(X, low, mid, high) //合并问题解, O(n)
	S_max <- max{S1, S2, S3}                  //合并问题解
	return S_max
end

时间复杂度
在这里插入图片描述
由递归树方法得,递归树深度 l o g n logn logn,每层代价为 n n n,整体时间复杂度为 T ( n ) = n l o g n T(n)=nlogn T(n)=nlogn


动态规划(DP)

概要

在这里插入图片描述

0-1背包问题

在这里插入图片描述
1. 蛮力枚举法:
在这里插入图片描述
递归式关系:
K n a p s a c k S R ( h , i , c ) = m a x { K n a p s a c k S R ( h , i − 1 , c − v i ) + p i , K n a p s a c k S R ( h , i − 1 , c ) } KnapsackSR(h, i, c) = max\{ KnapsackSR(h, i-1, c-v_i)+p_i, KnapsackSR(h, i-1, c)\} KnapsackSR(h,i,c)=max{KnapsackSR(h,i1,cvi)+pi,KnapsackSR(h,i1,c)}
伪代码:

KnapsackSR(h, i, c)  //从第h个到第i个商品中, 容量为c时最优解
输入:商品集合{h, ..., i}, 背包容量c
输出:最大总价格 P
if c < 0 then                       //超出背包容量
	return -∞
end
if i <= h-1 then                    //所有商品已决策完成
	return 0
end
P1 <- KnapsackSR(h, i-1, c-v_i)     //选择商品i
P2 <- KnapsackSR(h, i-1, c)         //不选择商品i
P <- max{P1 + p_i, P2}              //确定最优解
return P

在这里插入图片描述

2. 带备忘的递归
为了减少 蛮力枚举重复计算 问题,建立备忘录
在这里插入图片描述
伪代码:

KnapsackMR(i, c)  //前i个商品中, 容量为c时最优解
输入:商品集合{1, ..., i}, 背包容量c
输出:最大总价格 P[i, c]
P[i, c]表示在前i个商品中选择,背包容量为c时的最优解
if c < 0 then                    //超出背包容量
	return -∞
end
if i <= 0 then                   //所有商品已决策完成
	return 0
end
if P[i, c] != NULL then
	return P[i, c]               //避免重复计算
end
P1 <- KnapsackMR(i-1, c-v_i)     //选择商品i
P2 <- KnapsackMR(i-1, c)         //不选择商品i
P[i, c] <- max{P1 + p_i, P2}     //构造备忘录
return P[i, c]

在这里插入图片描述

3. 递推计算
忽略掉 带备忘录递归自顶向下 过程的优化方法,直接计算备忘录 P [ i , c ] P[i, c] P[i,c]
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最优解追踪:
① 倒叙判断是否选择商品
② 根据选择结果,确定最优子问题
在这里插入图片描述
在这里插入图片描述
伪代码

KnapsackDP(n, p, v, C)  //前i个商品中, 容量为c时最优解
输入:商品数量n, 各商品的价值p, 各商品的体积v, 背包容量C
输出:商品总价格的最大值, 最优解方案
创建二维数组 P[0..n, 0..C] 和 Rec[0..n, 0..C]  // 初始化
for i <- 0 to C do
	P[0, i] <- 0
end
for i <- 0 to n do
	P[i, 0] <- 0
end
//求解表格
for i <- 1 to n do   //依次计算子问题
|   for c <- 1 to C do  
|   |	if (v[i] <= c) and (p[i] + P[i-1, c-v[i]] > P[i-1, c]) then  //选择商品i
|	|		P[i, c] <- p[i] + P[i-1, c-v[i]]  //记录价格和决策
|	|		Rec[i, c] <- 1
|	|	end
|	|	else  //不选商品i
|	|		P[i, c] <- P[i-1, c]
|	|		Rec[i, c] <- 0
|	|	end
|	end
end
// 输出最优解方案
K <- C
for i <- n to 1 do   //倒序判断是否选择是否选择该商品
|	if Rec[i, K] = 1 then
|		print 选择商品i
|		K <- K-v[i]    //回溯子问题
|	end
|	else
|		print 不选商品i
|	end
end
return P[n, C]

时间复杂度:
求解表格阶段,整体复杂度为 O ( C ∗ n ) O(C*n) O(Cn),其中 n 是商品数量,C 是背包容量

总结:
在这里插入图片描述
在这里插入图片描述


贪心(Greedy)

概要

步骤:① 提出贪心策略 → ② 证明策略正确

部分背包问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
↑替换后单位体积的价值均不减少,故贪心解不劣于最优解(或者说贪心解即为最优解)

伪代码:

输入:商品数量 n, 各商品的价值 p, 各商品的体积 v, 背包容量 C
输出:商品价格的最大值, 最优解方案

计算商品性价比 Radio[1..n] 并按降序排序
// Radio[i], p[i], v[i]分别表示性价比第i大的商品的性价比、价格和体积
i ← 1
ans ← 0
while C > 0 and i <= n do
    if v[i] <= C then
        选择商品
        ans ← ans + p[i]
        C ← C - v[i]
    end
    else
        选择C体积的商品i
        ans ← ans + p[i] * (C / v[i])
        C ← 0
    end
    i ← i + 1
end
return ans

算法时间复杂度:
排序部分时间复杂度 O ( n l o g n ) O(n log n) O(nlogn),贪心求解部分时间复杂度 O ( n ) O(n) O(n),整体时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)

对比0-1背包问题:(需要用DP算法求解)
在这里插入图片描述

Huffman编码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果
在这里插入图片描述
在这里插入图片描述

输入:字符数 n, 各字符频数 F
输出:霍夫曼编码数

将F递增排序(字符频数按从小到大排序)
新建节点数组 P[1..n] 和 Q[1..n]  //初始化结点数组
for i ← 1 to n do
    P[i].freq ← F[i]
    P[i].left ← NULL
    P[i].right ← NULL
end
Q ← {}
for i ← 1 to n-1 do  //共需合并n-1次
    x ← ExtractMin(P, Q)  //选择频数最小的结点
    y ← ExtractMin(P, Q)
    z.freq ← x.freq + y.freq  //合并两个选择的结点
    z.left ← x
    z.right ← y
    Q.Add(z)  //存储合并后的结点
end
return ExtractMin(P, Q)  //返回编码树的根节点

算法时间复杂度:
排序部分时间复杂度 O ( n l o g n ) O(n log n) O(nlogn),初始化部分时间复杂度 O ( n ) O(n) O(n),数组开头查找最小值、合并、存储共计 O ( n ) O(n) O(n),整体时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)

活动选择问题

在这里插入图片描述
在这里插入图片描述
证明贪心算法得不到最优解,举一反例即可:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
伪代码:

输入:活动集合 S = {a_1, a_2, ..., a_n}, 每个活动a_i的起止时间s_i, f_i
输出:不冲突活动的最大子集 S'

把活动按照结束时间升序排序
S' ← {a_1}                  //把最早结束的活动加入到集合
k ← 1                       //记录当前选择的活动
for i ← 2 to n do           //检查每个活动
    if s_i >= f_k then      //如果没有冲突,则加入子集
        S' ← S'∪{a_i}
        k ← i               //更新当前选择的活动
    end
end
return S'

算法时间复杂度:
排序部分时间复杂度 O ( n l o g n ) O(n log n) O(nlogn),贪心求解部分时间复杂度 O ( n ) O(n) O(n),整体时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn)

带权重的活动选择问题:不适用贪心算法,需要用动态规划算法解决
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值