原题
点此链接1
题目分析
本题其实一共有三个点:
- 不等长编码可以得到多个最优解,其带权路径长度(WPL)是一致的
- 编码树所有的字符必须是叶子结点
- 所有的字符不存在二义性(即一个叶子结点只能表示一个字符)
程序1
按照课本(高等教育出版社 - 陈越 - 《数据结构》)P153部分,根据上述三个点分别计算最优解的WPL,随后根据递交的作业计算其WPL并进一步判断是否满足点2和点3。
这种方法比较死板,程序书写较多,不适合测试,但通用性更强。
#include <iostream>
#include <memory>
#include <list>
#include <queue>
#include <map>
#include <string>
using namespace std;
class Node;
typedef shared_ptr<Node> PNode;
class Node
{
public:
char v = 0;
int f = -1;
PNode l;
PNode r;
PNode fa;
Node() = default;
Node(char c, int f) : v(c), f(f) {}
};
int Height(PNode node)
{
int counter = 0;
while (node->fa)
{
counter++;
node = node->fa;
}
return counter;
}
int Freq(PNode node)
{
auto res = node->f * Height(node);
return res;
}
int Check(PNode node, map<char, int> raw)
{
char s;
string code;
std::cin >> s >> code;
auto sum = code.size() * raw.at(s);
for (const auto r : code)
{
// 0 in the left and 1 in ther right
if (r == '0')
{
// if the node is empty
if (!node->l)
node->l = make_shared<Node>(Node('\n', 0));
node = node->l;
}
else
{
// if the node is empty
if (!node->r)
node->r = make_shared<Node>(Node('\n', 0));
node = node->r;
}
}
if (node->v != '\n')
{
//cout << string(10,'*') << "\trepeat" << endl;
return -1;
}
if (node->l || node->r)
{
//cout << string(10,'*') << "\tnot leaf" << endl;
return -1;
}
node->v = s;
return sum;
}
int main()
{
int min = 0;
int charSum;
map<char, int> raw;
{
list<PNode> data;
std::cin >> charSum;
for (auto i = 0; i < charSum; i++)
{
char charTmp;
int intTmp;
std::cin >> charTmp >> intTmp;
data.push_back(make_shared<Node>(Node(charTmp, intTmp)));
raw.insert({charTmp, intTmp});
}
// create huffman tree to calculate the optimal size
while(data.size()>1)
{
data.sort([](PNode pl, PNode pr) { return pl->f < pr->f; });
auto a = data.front();
data.pop_front();
auto b = data.front();
data.pop_front();
auto sum = a->f + b->f;
data.push_back(make_shared<Node>(Node('\n', sum)));
data.back()->l = a;
data.back()->r = b;
a->fa = data.back();
b->fa = data.back();
}
queue<PNode> q;
q.push(data.front());
while (!q.empty())
{
auto sub = q.front();
q.pop();
if (!sub->l && !sub->r)
min += Freq(sub);
else
{
if (sub->l)
q.push(sub->l);
if (sub->r)
q.push(sub->r);
}
}
}
int n;
std::cin >> n;
vector<bool> resBool(n, true);
for (auto i = 0; i < n; i++)
{
int sum = 0;
auto head = make_shared<Node>(Node('\n', -1));
for (auto j = 0; j < charSum; j++)
{
// 1.check 1, if all the node in the leaf node.
auto res = Check(head, raw);
if (res == -1)
resBool[i] = false;
sum += res;
}
// 2. check 2, if it is optimal of the sum
if (sum != min)
{
//cout << string(10,'*') << "\tnot the optimal" << endl;
resBool[i] = false;
}
}
for (bool r : resBool)
if (r)
cout << "Yes" << endl;
else
cout << "No" << endl;
return 0;
}
程序2
程序可以优化,参考这篇博文2,可以得知哈弗曼树的WPL计算并不需要构造哈弗曼树然后计算。只需要计算所有的非叶子结点权值和即可。
参考这篇博文3的第二种解法,可以确定C++标准库中可以用优先序列定义最小堆随后再计算WPL。
#include <functional>
#include <iostream>
#include <memory>
#include <list>
#include <queue>
#include <map>
#include <string>
using namespace std;
class Node;
typedef shared_ptr<Node> PNode;
class Node
{
public:
PNode l;
PNode r;
char v = 0;
};
int Check(PNode node, map<char, int> raw)
{
char s;
string code;
std::cin >> s >> code;
for (const auto r : code)
{
// 0 in the left and 1 in ther right
if (r == '0')
{
if (!node->l)
node->l = make_shared<Node>(Node());
node = node->l;
}
else
{
if (!node->r)
node->r = make_shared<Node>(Node());
node = node->r;
}
}
if (node->v)
{
// cout << string(10, '*') << "\trepeat" << endl;
return -1;
}
if (node->l || node->r)
{
// cout << string(10, '*') << "\tnot leaf" << endl;
return -1;
}
node->v = s;
return code.size() * raw.at(s);
}
int main()
{
map<char, int> raw; // raw data
auto wsl = 0; // wsl
{
priority_queue<int,vector<int>,greater<>> fre;
auto num = 0;
cin >> num;
for (auto i = 0; i < num; i++)
{
pair<char, int> pairTmp;
std::cin >> pairTmp.first >> pairTmp.second;
fre.push(pairTmp.second);
raw.insert(pairTmp);
}
// calculate the wsl
while (fre.size() > 1)
{
auto a = fre.top();
fre.pop();
auto b = fre.top();
fre.pop();
wsl += a + b;
fre.push(a + b);
}
}
int n;
std::cin >> n;
vector<bool> resBool(n, true);
for (auto i = 0; i < n; i++)
{
auto sum = 0;
auto head = make_shared<Node>(Node());
for (size_t j = 0; j < raw.size(); j++)
{
// 1.check 1, if all the node in the leaf node.
auto res = Check(head, raw);
if (res == -1)
resBool[i] = false;
sum += res;
}
// 2. check 2, if it is optimal of the sum
if (sum != wsl)
{
// cout << string(10, '*') << "\tnot the optimal" << endl;
resBool[i] = false;
}
}
for (bool r : resBool)
if (r)
cout << "Yes" << endl;
else
cout << "No" << endl;
return 0;
}