解决VS2013不默认支持多字符编码的问题,有代价

本文详细介绍了如何在Visual Studio 2013中解决MFC项目使用MBCS编码导致的界面风格变化问题,并提供了界面美化的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引文链接:http://blog.youkuaiyun.com/shuaihj/article/details/17071351


编译错误类型:Building an MFC project for a non-Unicode character set is deprecated

微软解释:

    用于多字节字符编码 (MBCS) 的 MFC 库 (DLL) 不再包含于 Visual Studio 中,但是可用作插件,

    您可以在任何装有 Visual Studio Professional、Visual Studio Premium 或 Visual Studio Ultimate 的计算机上下载和安装。(在 Visual Studio 中,必须启用 MFC。)安装需要大约 440 MB 磁盘空间,包括英语(美国)和 DLL 的本地化版本。

    在安装 MBCS DLL 后,如果卸载或修复 Visual Studio,则还会卸载或修复。 但是,如果只禁用 MFC,MBCS DLL 仍会保留在系统上。 如果仅卸载或修复 MBCS DLL,将不会修改 Visual Studio。

    详细信息:http://blogs.msdn.com/b/vcblog/archive/2013/07/08/mfc-support-for-mbcs-deprecated-in-visual-studio-2013.aspx

            下载地址:http://www.microsoft.com/zh-cn/download/details.aspx?id=40770


这么做的一大牺牲就是,,,

好端端的VS2013  MFC 高大上界面变成了 VC6 的屌丝风格,瞬间变丑了!!!

所以还得去美化一下界面。。。


OVER


<think>我们已知哈夫曼编码的最短编码长度实际上就是哈夫曼树的带权路径长度(WPL),而计算WPL有两种常见方法: 方法1:按照定义,WPL等于所有叶子节点的权值乘以其到根节点的路径长度(即经过的边数)之和。 方法2:利用哈夫曼树的性质:所有非叶子节点的权值之和等于WPL。这是因为在构建哈夫曼树的过程中,每次合并两个节点都会产生一个新的节点,其权值为两个子节点权值之和,而合并的代价(即新节点的权值)会被累加,且这个合并的代价正好等于这两个子节点在最终的编码中被一位的代价(因为合并后深度增加一层,所以它们的编码长度都增加1,而增加的位数乘以权值就是新节点的权值)。因此,整个过程中所有非叶子节点的权值之和就是WPL。 根据引用[4]中的思路2,我们也可以知道:求哈夫曼编码的最短长度就是求哈夫曼树的非根结点权值之和(即所有非叶子结点的权值之和)。 因此,我们可以这样实现: 1. 统计每个字符出现的频率(权值)。 2. 使用优先队列(最小堆)来构建哈夫曼树:每次从堆中取出两个最小的权值,合并成一个新节点(权值为这两个权值之和),并将新节点放入堆中,直到堆中只剩下一个节点(根节点)。 3. 在构建树的过程中,我们并需要保存整棵树的结构,因为我们只需要计算WPL。根据方法2,我们只需要在每次合并两个节点时,将合并后的新权值(即两个子节点的权值之和)累加起来,最后这个累加和就是WPL。 注意:方法2的正确性在于,每次合并的两个节点,在最终的树中都会增加一层的深度(相对于合并后的新节点而言),而合并的代价(新节点的权值)就等于这两个节点的权值之和,这个值正好等于这两个节点在最终WPL中因为深度增加而出来的部分(即权值*1)。而整个过程中,每个叶子节点被合并的次数就是它的深度,所以所有非叶子节点的权值之和就等于WPL。 因此,我们可以这样计算: WPL = 所有非叶子节点的权值之和 = 每次合并两个节点时,将两个节点的权值相加,并将这个和累加,直到最后。 举个例子:有3个节点,权值分别为1,2,3。 第一步:合并1和2,得到新节点权值为3(此时累加3)。此时堆中有两个节点:3(原来的)和3(新的)。 第二步:合并两个3,得到新节点6(累加3+3=6)。此时堆中只剩下6。 总累加和=3+6=9。而WPL=1*2 + 2*2 + 3*1 = 2+4+3=9(注意:这里叶子节点的深度是从根节点往下数,根节点深度0,下一层深度1,再下一层深度2;或者也可以从根节点到叶子节点的边数,那么深度为1的节点有两条边,深度为2的节点有一条边?实际上,我们通常用节点深度(从根到该节点的边数)来计算。在这个例子中,权值为1和2的节点深度为2(因为合并了两次),权值为3的节点深度为1(合并了一次)。所以WPL=1*2+2*2+3*1=2+4+3=9。 因此,我们只需要在构建哈夫曼树的过程中累加每次合并的权值(即新节点的权值)即可得到WPL。 步骤: 1. 读取字符串,统计每个字符出现的次数(权值)。 2. 将所有的权值放入一个最小堆(优先队列)中。 3. 初始化WPL=0。 4. 当堆中的节点个数大于1时: a. 取出堆顶的两个最小权值(假设为a和b)。 b. 计算合并后的权值sum = a+b。 c. 将sum累加到WPL中(即WPL += sum)。 d. 将sum放入堆中。 5. 当堆中只剩下一个节点时,WPL即为所求。 注意:如果字符串只有一个字符,那么WPL应该是0吗?但是根据定义,一个字符需要编码(或者编码长度为0),但通常我们规定,如果只有一个字符,那么编码长度应该为1(因为至少需要一位来表示,否则无法区分)。但是,根据哈夫曼树,只有一个节点时,我们会进行合并,所以WPL=0。这显然符合实际。所以我们需要特殊处理:当只有一个字符时,编码长度应该是1(即深度为1,但是只有一个节点时,我们通常把它看作根节点,深度为0?这里需要明确)。 实际上,哈夫曼编码要求每个字符对应一个叶子节点。当只有一个字符时,我们构建的树只有一个节点,那么它的编码长度应该是0(因为需要任何二进制位就可以表示,但这样在解码时会有问题)。因此,在实际应用中,我们通常要求至少有两个字符。但题目没有说明,我们按照一般情况处理。 但是,根据题目“给定一个字符串”,如果字符串只有一个字符,那么最短编码长度应该是1(因为至少需要1位来表示,比如0或1)。所以,我们可以在统计字符频率后,如果只有一个字符,那么直接返回1(因为只有一个字符,我们用一个二进制位表示,比如0)。但是,哈夫曼编码对于单个字符的编码长度应该是0?这显然符合实际需求。所以,我们这里按照实际需求调整:如果字符串只有一个字符,那么最短编码长度就是1。 然而,根据哈夫曼树的构建,当只有一个节点时,我们无法合并,所以按照上述算法,WPL=0。因此,我们需要在最后判断:如果初始的权值个数为1(即只有一个字符),那么直接返回该字符出现的次数(即字符串长度)乘以1?对,因为字符串长度就是出现的次数,比如字符串"a"出现5次,那么我们希望用1位来表示每个字符,那么总长度应该是5,但WPL=0。所以我们需要调整。 实际上,我们计算的是整个字符串的编码长度,即每个字符的编码长度乘以出现次数的总和。对于单个字符,我们规定编码长度为1(即使哈夫曼树只有一个节点,但实际传输中我们至少需要1位来表示)。因此,我们可以这样处理: 如果字符串为空,返回0。 如果字符串只有一个同的字符(即频率数组大小为1),那么总编码长度 = 字符串长度(因为每个字符用1位表示,所以总位数等于字符个数)。 但是,我们也可以这样理解:在构建哈夫曼树时,如果只有一个节点,那么我们可以认为它被合并了0次,所以WPL=0。但是实际编码长度应该是1*(出现次数)。所以,我们可以在算法中特殊处理,而是将单个节点的情况单独处理:在构建哈夫曼树之前,如果堆中只有一个节点,那么WPL=该节点的权值(因为每个字符需要1位,总位数=权值*1,而权值就是出现次数,所以总位数=权值)。但是,按照哈夫曼树,我们并需要这样,因为哈夫曼树要求至少两个节点。 因此,我们修改算法: 当堆中节点数大于1时,我们才进行合并。如果堆中只有一个节点,那么WPL就是该节点的权值(因为此时,我们只能用一个二进制位来表示这个字符,每个字符占1位,总位数=出现次数)。 但是,这个处理并符合哈夫曼树的WPL定义。因为按照定义,单个叶子节点的路径长度为0,所以WPL=0。所以我们需要在最后输出整个字符串的编码长度时,实际上应该是WPL(即0)吗?显然是。 所以,我们重新审视:题目要求的是“哈夫曼编码的最短长度”,即整个字符串经过哈夫曼编码后所占的总位数。对于单个字符,我们无法用0位来表示(因为这样无法区分),所以实际应用中,我们必须用至少1位来表示。因此,我们规定:当只有一个字符时,我们给这个字符分配一个长度为1的编码(比如0)。那么总位数=1*(出现次数)。 因此,我们可以这样实现: 在构建哈夫曼树之前,如果频率数组大小为1,那么直接返回字符串长度(即出现次数,因为每个字符占1位,总位数=出现次数)。 但是,如果频率数组大小大于1,我们按照上述合并过程得到WPL,这个WPL就是整个字符串的编码总位数。 所以,算法步骤: 1. 统计字符串中每个字符出现的次数(权值)。 2. 如果字符串为空,返回0。 3. 将所有的权值放入最小堆(优先队列)中。 4. 初始化WPL=0。 5. 如果堆中只有一个节点,那么WPL = 该节点的权值(即字符串长度)。 6. 否则,循环直到堆中只有一个节点: a. 取出堆顶的两个最小权值a和b。 b. 合并sum = a+b。 c. WPL += sum。 d. 将sum放入堆中。 7. 输出WPL。 注意:这样处理,当只有一个节点时,我们直接返回权值(即字符串长度),相当于每个字符用1位表示,总位数=字符串长度。当有个节点时,我们返回的WPL就是整个字符串的编码总位数。 验证:例子"aaabbbcc",字符频率:a:3, b:3, c:2。 堆:[2,3,3] 第一步:取出2和3,合并为5,WPL=5。堆变为[3,5]。 第二步:取出3和5,合并为8,WPL=5+8=13。堆变为[8]。 所以WPL=13。 计算每个字符的编码:假设a:00, b:01, c:1(或者a:01, b:00, c:1,等等),那么总位数=3*2+3*2+2*1=6+6+2=14?对,为什么是13呢? 注意:我们计算的是非叶子节点的权值之和:5(2+3)和8(3+5),所以5+8=13。但是,实际总编码长度应该是少? 按照哈夫曼树:两个最小权值2和3合并(权值5),然后5和3合并(权值8)。所以树的结构是: 根(8) / \ c(2) (5) / \ a(3) b(3) 那么,c的编码长度为1,a和b的编码长度为2。总编码长度=3*2+3*2+2*1=6+6+2=14。 为什么我们计算的非叶子节点权值之和是13?因为非叶子节点只有5和8,5+8=13。而14和13相等。 这里我们发现了问题:实际上,在合并过程中,我们每次合并的两个节点,它们的权值被累加,但是每个叶子节点的深度(即编码长度)等于它被合并的次数。而每个非叶子节点的权值等于其左右子树权值之和,所以非叶子节点的权值之和等于所有叶子节点的权值乘以各自被合并的次数(注意:被合并的次数等于深度?一定,因为每个叶子节点在每次合并时都会贡献一次权值,而它被合并了几次,它的权值就被加了几次?) 实际上,我们重新计算:叶子节点c(权值2)被合并了一次(变成5的一部分),然后5又被合并了一次(变成8的一部分),所以c被合并了2次?对,c在第一次合并后成为5的左孩子,然后5再被合并,此时c的深度增加了2(从根节点到c需要经过两条边)。同样,a和b在第一次合并时没有被合并(它们是在第二步合并的?对,第一步合并的是c和a?对,第一步我们合并的是最小的两个:2和3(最小的两个是2和3,这里3是a还是b?我们并没有区分,假设第一步合并了c(2)和a(3)得到5,然后第二步合并5和b(3)得到8。那么: c被合并了1次(在第一步),然后5(包含c和a)又被合并了一次(第二步),所以c的深度是2(从根节点8->5->c,经过两条边)。 a也是被合并了两次:第一步合并成5,第二步5又被合并成8,所以深度也是2。 b只被合并了一次:第二步合并时被加入,所以深度为1(从根节点8直接到b?对,根节点8的左孩子是5,右孩子是b?这样树的结构是: 8 / \ 5 b(3) / \ c(2) a(3) 那么b的深度为1,c和a的深度为2。总编码长度=2*2+3*2+3*1=4+6+3=13。这样就和WPL=13相等了。 所以,我们之前对树的构建有误解:在第二步合并时,我们取出了堆中剩下的两个节点:5和3(b),合并成8。所以树的结构是:8的左右孩子分别是5和b(权值3)。而5的左右孩子分别是c(2)和a(3)。所以: a: 00 (从根8->5->a) c: 01 (从根8->5->c) b: 1 (从根8->b) 总编码长度=3(a出现3次,编码长度2,所以3*2=6) + 2(c出现2次,编码长度2,2*2=4) + 3(b出现3次,编码长度1,3*1=3)=6+4+3=13。 所以,非叶子节点权值之和=5(第一次合并:2+3)+8(第二次合并:5+3)=13,等于总编码长度。 因此,上述算法正确。 但是,为什么之前的例子(1,2,3)计算出来是9,而实际计算也是9?所以算法正确。 因此,我们按照这个思路实现C++代码。 步骤: 1. 读入字符串。 2. 统计频率(可以用map)。 3. 将频率值(权值)放入优先队列(小顶堆)。 4. 如果队列为空,返回0。 5. 如果队列中只有一个节点,那么返回该节点的权值(即字符串长度,因为只有一个字符,每个字符用1位,总位数=字符个数)。 6. 否则,循环:每次弹出两个最小的,求和,累加到WPL,然后将和压入队列,直到队列中只剩下一个节点。 7. 输出WPL。 注意:优先队列默认是最大堆,我们可以使用greater<int>来使其成为最小堆。 代码实现: 例如:输入字符串"aaabbbcc",输出13。 我们写一个函数,输入字符串,输出哈夫曼编码的总位数(即WPL,也就是最短编码长度)。 下面我们用C++实现。</think>### 哈夫曼编码的最短编码长度及C++实现 哈夫曼编码是一种基于字符出现频率构建的最优前缀编码,其核心是通过构建哈夫曼树实现。最短编码长度即哈夫曼树的带权路径长度(WPL),计算公式为: $$ \text{WPL} = \sum (\text{字符频率} \times \text{编码长度}) $$ 根据哈夫曼树性质,**WPL等于所有非叶子节点权值之和**[^4]。以下是C++实现步骤: --- #### 算法步骤 1. **统计字符频率** 遍历字符串,记录每个字符的出现次数。 2. **构建最小堆** 将所有频率放入优先队列(最小堆)。 3. **合并节点计算WPL** - 当堆中节点数>1时,反复取出两个最小频率节点合并 - 新节点权值 = 两节点权值之和 - 将新节点权值累加到WPL - 新节点重新入堆 4. **处理特殊情况** 若字符串仅含一种字符,WPL=字符串长度(每个字符用1位表示)。 --- #### C++代码实现 ```cpp #include <iostream> #include <queue> #include <unordered_map> #include <vector> using namespace std; int huffmanWPL(string s) { if (s.empty()) return 0; // 统计字符频率 unordered_map<char, int> freqMap; for (char c : s) { freqMap[c]++; } // 构建最小堆 priority_queue<int, vector<int>, greater<int>> minHeap; for (auto& pair : freqMap) { minHeap.push(pair.second); } int wpl = 0; // 处理单一字符情况 if (minHeap.size() == 1) { wpl = minHeap.top(); } // 合并节点计算WPL while (minHeap.size() > 1) { int a = minHeap.top(); minHeap.pop(); int b = minHeap.top(); minHeap.pop(); int sum = a + b; wpl += sum; // 累加非叶子节点权值 minHeap.push(sum); } return wpl; } int main() { string s = "aaabbbcc"; cout << "字符串: " << s << endl; cout << "最短编码长度: " << huffmanWPL(s) << endl; // 输出13 return 0; } ``` --- #### 关键点说明 1. **时间复杂度**:$O(n \log n)$,其中 $n$ 为字符种类数 2. **空间复杂度**:$O(k)$,$k$ 为同字符数量 3. **正确性验证**: - 输入 `"aaabbbcc"` 时: - 频率:`a:3, b:3, c:2` - 合并过程:`2+3=5` → `3+5=8` → `WPL=5+8=13` - 编码:`a=00, b=1, c=01` → 总长度 $3\times2 + 3\times1 + 2\times2=13$ 4. **特殊处理**:单字符字符串(如 `"aaaa"`)直接返回长度 `4`[^4] > 此实现直接计算WPL,无需构建完整哈夫曼树结构,效率更高[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值