hdu1053 entropy【哈夫曼】
poj3253 fence repair【貪心/哈夫曼+反向思維】
codeforces884d boxes and balls【三叉哈夫曼+反向思維】
思考:為什麼權值為字元出現次數時哈夫曼編碼長度等於帶權路徑長度
傳送門:hdu2527 safe or unsafe
題目說編碼方式是哈夫曼編碼(huffman coding),並定義乙個字母的權值等於該字母在字串**現的頻率。要求判斷哈夫曼編碼值和給定的安全值大小(此處說的編碼值即為哈夫曼的帶權路徑長度)。直接統計字母出現次數,求哈夫曼樹帶權路徑長度,並比較帶權路徑長度和安全值大小即可。
#include
#include
#include
#include
#include
using
namespace std;
const
int maxn=
6e5+5;
char s[maxn]
;int cnt[30]
;// 用於統計字母出現次數
priority_queue<
int,vector<
int>
,greater<
int>
> q;
intmain()
if(sum==0)
// 特判只有乙個字母的情況
sum=q.
top();
if(sum<=n)
printf
("yes\n");
else
printf
("no\n");
while
(!q.
empty()
)// 清空佇列
q.pop();
memset
(cnt,0,
sizeof
(cnt));
// 清除統計
}return0;
}
傳送門:hdu1053 entropy
給定只包含大寫字母和『_'的字串(乙個一行,輸入到end結束)。問:每個字母8位,這個字串長多少位?若採用哈夫曼編碼,編碼長度多少位?壓縮率為多少(保留1位小數)?
要求哈夫曼編碼長度,直接求帶權路徑長度即可,權值為字元出現次數。(若要求實際編碼,需構建哈夫曼樹,有興趣的可以了解一下。更多時候使用的是哈夫曼的思想)
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
const
int maxn=
6e5+5;
char s[maxn]
;int cnt[35]
;priority_queue<
int,vector<
int>
,greater<
int>
> q;
intmain()
if(sum==0)
// 特判只有乙個字元的情況
sum=q.
top();
printf
("%d %d %.1lf\n"
,len*
8,sum,len*8*
1.0/sum)
;while
(!q.
empty()
)//清空佇列
q.pop();
memset
(cnt,0,
sizeof
(cnt));
// 清空計數陣列
}return0;
}
傳送門:poj3253 fence repair
將一塊木板分割成n塊,每塊長度為l[i]。每切割一次,代價為被分開的兩塊木板長度總和。例如,將一塊木板切割成8、5、8的三塊木板。首先將木板切割成13、8時,代價為21,再將長度為13的木板切割成長度5、8時,代價為13。於是合計代價為34。
問:按題目要求將木板切割出n塊,最小的代價是多少?
反向還原,將最終被分割的每塊木板還原為完整的一根木塊,每次還原的代價就是兩塊木板拼接後的總長度。要使得還原代價最小,每次拼接的木板長度應該是最小的。每次拼接後,後面再拼接還需要計算當前木塊的長度。所以可以轉化為求哈夫曼帶權路徑長度的問題。
因為(1 ≤ n ≤ 20000) ,(1 ≤ l[i] ≤ 50000),最多20000塊木板,每塊最長為50000,累積求和會超出int,所以要用long long儲存。
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
priority_queue
,greater
> q;
intmain()
if(n==1)
// 一塊木板,無需切割
ll sum=0;
while
(q.size()
>1)
printf
("%lld\n"
,sum)
;return0;
}
傳送門:codeforces884d boxes and balls
給定乙個n。然後是n個不同顏色的球的數目。第 i 種顏色的球最終要放入第 i 個盒子中。開始所有的球都在第乙個盒子裡。每次可以進行以下兩步操作:
1、把某乙個非空盒子中的球全部拿出來。
2、選個k個空盒子,將拿出來的球分為k組,分別放入k個空盒子。(k可以取值為2或3)
每次操作的代價為拿出的球的數量。
問:將n種不同顏色的球放入n個對應的盒子花費最小是多少?
開始想到的是每次貪心前兩大的數,但這不能構成最優解。這個思路對於樣例
614
4444
處理的順序是21->13 4 4->5 4 4 4 4->1 4 4 4 4 4,ans=21+13+5=39.
但是如果我們按照21->9 8 4->9 4 4 4->1 4 4 4 4 4的順序處理的話,ans=21+8+9=38.
逆向思考,把n個盒子裡的球放回第乙個盒子,每次的操作代價是k個盒子球數量的和。那麼就轉化為了構造哈夫曼樹。k可能為2或3,直接合併3個的代價更少,所以我們要使得k=3。當奇數時,能構成乙個完整的三叉哈夫曼樹,當偶數時,添乙個0結點,在不影響結果的情況下強行構成三叉哈夫曼樹。
注意:(1 ≤ n ≤ 200000) , (1 ≤ ai ≤ 109),要使用long long。
#include
#include
#include
#include
#include
using
namespace std;
typedef pair<
int,
int> p;
typedef
long
long ll;
const
int maxn=
6e5+5;
priority_queue
,less
> q;
intmain()
ll ans=sum;
while
(q.size()
>3)
while
(!q.
empty()
) q.
pop();
printf
("%lld\n"
,ans)
;return0;
}
#include
#include
#include
#include
#include
using
namespace std;
typedef
long
long ll;
priority_queue
,greater
> q;
intmain()
if(n==1)
// 只有乙個盒子,無需操作if(
(n&1)==
0)// 當 n為偶數,補 0 湊成奇數使得可以每次都取三個
q.push(0
);ll sum=0;
while
(q.size()
>1)
printf
("%lld\n"
,sum)
;return0;
}
因為:
哈夫曼帶權路徑長度(wpl) = sum(葉結點的權值 × 該結點到根結點的路徑長度)
哈夫曼編碼長度 = sum(字元出現次數 × 該字元編碼長度)
又:字元編碼長度 == 字元到根結點的路徑長度
葉節點權值 == 某字元出現次數
所以:哈夫曼帶權路徑長度(wpl) == 哈夫曼編碼長度
哈夫曼樹與哈夫曼編碼
在一般的資料結構的書中,樹的那章後面,著者一般都會介紹一下哈夫曼 huffman 樹和哈夫曼編碼。哈夫曼編碼是哈夫曼樹的乙個應用。哈夫曼編碼應用廣泛,如 jpeg中就應用了哈夫曼編碼。首先介紹什麼是哈夫曼樹。哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的...
哈夫曼樹與哈夫曼編碼
1.哈夫曼 huffman 編碼是哈夫曼樹的乙個應用。2.哈夫曼樹又稱為最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點的相對值乘上其到根結點的路徑和權值。3.影象jpg就是利用了哈夫曼編碼。4.哈夫曼樹是最優二叉樹,子樹有左右次序之分。5.哈夫曼樹的形態不是唯...
哈夫曼樹與哈夫曼編碼
哈夫曼樹是一種簡單的樹結構,建樹過程如下 給出一組資料,不斷選擇最小的兩個數,並用兩個數的和作為它們的parent節點,再從資料中刪除這兩個數,將兩個數的和加入資料中,直到所有的資料都被加入樹結構,形成一顆樹。這顆樹的所有非葉子節點都有兩個child,兩個child的值的和則是這個節點的值,根節點是...