您的位置:首页 > 其它

《算法导论》第16章 贪心算法 个人笔记

2017-06-01 12:59 148 查看

第16章 贪心算法

16.1 活动选择问题

问题:假设有一个n个活动的集合S=a1,a2,...,an,这些活动使用同一个资源,而这个资源在某个时刻只能供一个活动使用。每个活动都有一个开始时间si和一个结束时间fi。若si≥fi或sj≥fi,则ai和aj是兼容的。在活动选择问题中,我们希望选出一个最大兼容活动集。假设活动已按结束时间的单调递增顺序排序。

1、动态规划法

令Sij表示在ai结束之后开始。且在aj开始之前结束的那些活动的集合,用c[i,j]表示集合Sij的最优解的大小,故有

c[i,j]=0,ifSij=ϕc[i,j]=maxak∈Sijc[i,k]+c[h,j]+1,ifSij≠ϕ

2、贪心选择

定理:考虑任意非空子问题Sk,令am是Sk中结束时间最早的活动,则am在Sk的某个最大兼容活动子集中。

证明:令Ak是Sk的一个最大兼容子集,且aj是Ak中结束时间最早的活动。

若aj=am,即证。

若aj≠am,将Ak中的aj替换为am,因为fm≤fj,则还是兼容的,数目不变,故替换后的也是Sk的一个最大兼容活动子集,且包含am。

- 递归贪心算法

为方便初始化,添加一个虚拟活动a0,其结束时间f0=0

public List<Integer> RECURSIV_ACTIVITY_SELECTOR(int[] s,int[] f,int k,int n){
int m = k + 1;
while(m<=n && s[m]<=f[k])
m++;
List<Integer> res = new ArrayList<>();
if(m<=n)
return res.add(m).addAll(RECURSIV_ACTIVITY_SELECTOR(int[] s,int []f,int k,int n))
else
return res;
}


迭代贪心算法

public List<Integer> GREED_ACTIVITY_SELECTOR(int[] s,int[] f){
int n = s.length - 1; //去掉虚拟活动
List<Integer> res = new ArrayList<>();
res.add(1);
int k = 1;
for(int i = 2; i<=n; i++){
if(s[m]>=f[k]){
res.add(m);
k = m;
}
}
return res;
}


16.2 贪心算法原理

一般地,我们按如下步骤设计贪心算法:

1. 将最优化问题转化为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解。

2. 证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的。

3. 证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构。

贪心对动态规划

0-1背包问题:动态规划

分数背包问题:贪心

/**
* @param m 表示背包的最大容量
* @param n 表示商品个数
* @param w 表示商品重量数组
* @param p 表示商品价值数组
*/
public static int[][] Package1(int m, int n, int[] w, int[] p) {
//c[i][v]表示前i件物品恰放入一个重量为m的背包可以获得的最大价值
int c[][] = new int[n + 1][m + 1];
for (int i = 0; i < n + 1; i++)
c[i][0] = 0;
for (int j = 0; j < m + 1; j++)
c[0][j] = 0;

for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
//当物品为i件重量为j时,如果第i件的重量(w[i-1])小于重量j时,c[i][j]为下列两种情况之一:
//(1)物品i不放入背包中,所以c[i][j]为c[i-1][j]的值
//(2)物品i放入背包中,则背包剩余重量为j-w[i-1],所以c[i][j]为c[i-1][j-w[i-1]]的值加上当前物品i的价值
if (w[i - 1] <= j) {
if (c[i - 1][j] < (c[i - 1][j - w[i - 1]] + p[i - 1]))
c[i][j] = c[i - 1][j - w[i - 1]] + p[i - 1];
else
c[i][j] = c[i - 1][j];
} else
c[i][j] = c[i - 1][j];
}
}
return c;
}
//用一维数组存储
public static int Package2(int m, int n, int[] w, int[] p) {
int[] f = new int[m + 1];
for (int i = 1; i < f.length; i++)    //必装满则f[0]=0,f[1...m]都初始化为无穷小
f[i] = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
for (int j = f.length - 1; j >= w[i]; j--) {
f[j] = Math.max(f[j], f[j - w[i]] + p[i]);
}
}
return f[m];
}


在具体运用时,可以首先假设全部物品装进背包是否超载,若不超载则直接输出最优解。有一次某公司一道笔试题就是这样,直接上dq就超时gg了=。=

16.3 赫夫曼编码

赫夫曼编码用于压缩数据,根据每个字符的出现频率构造最优二进制表示。

前缀码:没有任何码字是其他码字的前缀。

构造赫夫曼编码,使用一个以属性freq为关键字的最小优先队列Q。

HUFFMAN(C)
n = C.length
Q = C
for i =1 to n-1
allocate a new node z
z.left = x = EXTRACT-MIN(Q)
z.right = y = EXTRACT-MIN(Q)
z.freq = x.freq + y.freq
INSERT(Q,z)
return EXTRACT-MIN(Q) //return the root of the tree


定理:过程HUFFMAN会生成一个最优前缀码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法导论