传送门: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;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d%s",&n,&s);
int len=strlen(s);
for(int i=0;i<len;i++)
cnt[s[i]-'a']++;
for(int i=0;i<26;i++)
if(cnt[i])
q.push(cnt[i]);
int sum=0;
while(q.size()>1)
{
int t1=q.top();
q.pop();
int t2=q.top();
q.pop();
sum+=t1+t2;
q.push(t1+t2);
}
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)); // 清除统计
}
return 0;
}
传送门: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;
int main()
{
while(scanf("%s",s)&&strcmp(s,"END"))// 读到 END为止
{
int len=strlen(s);
for(int i=0;i<len;i++)
cnt[s[i]-'A']++; // '_'ascll码为 95,'A'为 65,95-65=30
for(int i=0;i<31;i++)
if(cnt[i])
q.push(cnt[i]);
int sum=0;
while(q.size()>1)
{
int t1=q.top();
q.pop();
int t2=q.top();
q.pop();
sum+=t1+t2;
q.push(t1+t2);
}
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)); // 清空计数数组
}
return 0;
}
传送门: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<ll,vector<ll>,greater<ll> > q;
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int len;
scanf("%d",&len);
q.push(len);
}
if(n==1) // 一块木板,无需切割
{
puts("0");
return 0;
}
ll sum=0;
while(q.size()>1)
{
ll t1=q.top();
q.pop();
ll t2=q.top();
q.pop();
sum+=t1+t2; // 代价
q.push(t1+t2); // 后面拼接需再次计算
}
printf("%lld\n",sum);
return 0;
}
传送门:Codeforces884D Boxes And Balls
给定一个n。然后是n个不同颜色的球的数目。第 i 种颜色的球最终要放入第 i 个盒子中。开始所有的球都在第一个盒子里。每次可以进行以下两步操作:
1、把某一个非空盒子中的球全部拿出来。
2、选个k个空盒子,将拿出来的球分为k组,分别放入k个空盒子。(k可以取值为2或3)
每次操作的代价为拿出的球的数量。
问:将n种不同颜色的球放入n个对应的盒子花费最小是多少?
开始想到的是每次贪心前两大的数,但这不能构成最优解。这个思路对于样例
6
1 4 4 4 4 4
处理的顺序是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<ll,vector<ll>,less<ll> > q;
int main()
{
int n;
scanf("%d",&n);
ll sum=0;
for(int i=0;i<n;i++)
{
int num;
scanf("%d",&num);
q.push(num);
sum+=num;
}
ll ans=sum;
while(q.size()>3)
{
ll t1=q.top();
q.pop();
ll t2=q.top();
q.pop();
sum-=(t1+t2); // 每次确定两个最大的
ans+=sum;
}
while(!q.empty())
q.pop();
printf("%lld\n",ans);
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
priority_queue<ll,vector<ll>,greater<ll> > q;
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int num;
scanf("%d",&num);
q.push(num);
}
if(n==1) // 只有一个盒子,无需操作
{
puts("0");
return 0;
}
if((n&1)==0) // 当 n为偶数,补 0 凑成奇数使得可以每次都取三个
q.push(0);
ll sum=0;
while(q.size()>1)
{
ll t1=q.top();
q.pop();
ll t2=q.top();
q.pop();
ll t3=q.top();
q.pop();
ll temp=t1+t2+t3;
q.push(temp);
sum+=temp;
}
printf("%lld\n",sum);
return 0;
}
因为:
哈夫曼带权路径长度(WPL) = SUM(叶结点的权值 × 该结点到根结点的路径长度)
哈夫曼编码长度 = SUM(字符出现次数 × 该字符编码长度)
又:
字符编码长度 == 字符到根结点的路径长度
叶节点权值 == 某字符出现次数
所以:
哈夫曼带权路径长度(WPL) == 哈夫曼编码长度