第16章 貪心算法
16.1 活動選擇問題
問題:假設有一個n個活動的集合
1、動態規劃法
令
2、貪心選擇
定理:考慮任意非空子問題
證明:令
若
若
- 遞歸貪心算法
爲方便初始化,添加一個虛擬活動
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會生成一個最優前綴碼。