您的位置:首页 > 其它

贪心算法

2017-05-24 12:16 405 查看
贪心算法和动态规划有异曲同工之秒,能用贪心法的,就不用动规。我只想说,动态规划真TM难。

分数背包问题

Huffman编码

贪心算法这样一个算法,它在每一步都做出在当时看来是最优的方案,即它总是做出局部最优的选择。然后构成一个全局最优解。贪心算法也是一个强有力的算法,很多问题都能用贪心算法来解决。下面就说说两个经典的问题,分数背包问题和哈夫曼(Huffman)编码。

分数背包问题

分数背包问题是这样的,假设有一个背包体积为W, 有一系列商品,他们的体积各不相同,找出获取商品价值最大的方案。分数背包问题和0-1背包问题的惟一区别在于,是否可以取商品的一部分。贪心算法可以求解分数背包问题,但无法求解0-1背包问题。

正如贪心算法一样,人总是贪的,如果真有这样一件事,我们当然希望全部拿走,但是奈何空间有限。为简单起见,假设所有数据都是整数,该问题的输入和输出如下所述:

输入:

第一行是一个整数m,表示商品的数量

第二行是m个整数,表示商品的价值

输出:

最大价值v

定义p = v / w,我们称之为性价比,很明显,我们当然应该取性价比最高的商品,即价值最高,体积最小。

比如一个体积为50的背包,有三件商品,体积和价值分别为

商品(i)123
体积(v)102030
价值(w)60100120
性价比(p)654
所以我们先取商品1, 价值为60, 占10体积,还剩40体积,再取商品2,价值为100, 占20体积,还剩20体积,最后取20体积商品3,价值为4 * 20 = 80, 所以最优方案是60 + 100 + 80 = 240. 代码如下所示:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Objcect {
int c;
int v;
double p;
}Object;

int compare(const void* n1, const void* n2)
{
return ((struct Objcect*)n2)->p - ((struct Objcect*)n1)->p;
}

int main(void)
{
int m, w;

while(scanf("%d", &m), m)
{
struct Objcect* objs = (struct Objcect*)malloc(sizeof(struct Objcect) * m);

for(int i = 0; i != m; ++i)
{
scanf("%d %d", &objs[i].c, &objs[i].v);
objs[i].p = (double)objs[i].v / objs[i].c;
}

qsort(objs, m, sizeof(struct Objcect), compare);
scanf("%d", &w);

double value = 0;
for(int i = 0; i != m && w != 0; ++i)
{
if(objs[i].c <= w)
{
value += objs[i].v;
w -= objs[i].c;
}else
{
value += w * objs[i].p;
w = 0;
}
}

printf("%.2lf\n", value);
free(objs);
}

return 0;
}


输出结果如下:



Huffman编码

哈夫曼编码的实用性还挺高的,可以有效的压缩数据,哈夫曼编码是这样的,假设有一个文件,其中每个字符都有一个使用频率,可以给每个字符构造一个编码,达到压缩文件的目的,如下所示:

字符abcdef
频率4513121695
变长编码010110011111011100
哈夫曼编码就是一个贪心的过程,每次选择频率最小的两个数据,最终构成一棵哈夫曼树,过程如下:

初始状态,集合里是每个结点,首先选择「e」和「f」点,如下所示:



然后,构造一个结点,左右子树分别是这两个结点,结点的频率值为两者之和,如下所示:



重复上述过程,直至最终只有一个结点,如下所示:









从根结点开始,沿路径到根结点,左子树为0,右子树为1,即可得到各编码值。代码如下所示:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Node {
int freq;
char letter;
struct Node* leftChild;
struct Node* rightChild;
}Node;

int nodeCompare(const void* node1, const void* node2)
{
return (*(struct Node**)node1)->freq - (*(struct Node**)node2)->freq;
}

struct Node* generateHuffman(int m)
{
struct Node* root = NULL;
struct Node* List[m];
for(int i = 0; i != m; ++i)
{
struct Node* node = (struct Node*)malloc(sizeof(struct Node));
scanf("%s %d", &node->letter, &node->freq);
node->leftChild = NULL;
node->rightChild = NULL;
List[i] = node;
}

//集合中共有多少个元素
int total = m;

while(total > 1)
{
//按照频率高低进行排序
qsort(List, total, sizeof(struct Node*), nodeCompare);

struct Node* node1 = (struct Node*)List[0];
struct Node* node2 = (struct Node*)List[1];

if(node1 && node2)
{
struct Node* node = (struct Node*)malloc(sizeof(struct Node));
node->letter = -1;
node->freq = node1->freq + node2->freq;
node->leftChild = node1;
node->rightChild = node2;

//删除第一、第二两个结点,其他元素前移
for(int i = 2; i != total; ++i)
List[i - 2] = List[i];

//将最后一点加入到集合
List[total - 1] = NULL;
List[total - 2] = node;

--total;
root = node;    //修改根结点
}
}

return root;
}

//输出Huffman编码
//code,用于记录当前结点的编码值
void printHuffmanCode(struct Node* root, int* code)
{
//当前遍历结点索引
static int index = 0;

if(root == NULL)
return ;

//如果是往左走,记录结点值为0
if(root->leftChild != NULL)
{
code[index++] = 0;
printHuffmanCode(root->leftChild, code);
}

//如果是往右走,记录结点值为1
if(root->rightChild != NULL)
{
code[index++] = 1;
printHuffmanCode(root->rightChild, code);
}

//如果是有效结点
if(root->letter != -1)
{
//输出当前字符
printf("%c : ", root->letter);

//输出当前编码
for(int i = 0; i != index; ++i)
printf("%d", code[i]);
printf("\n");
}

//指针回溯
code[--index] = -1;
return ;
}

//递归删除Huffman树
void destroyTree(struct Node* root)
{
if(root->leftChild != NULL)
destroyTree(root->leftChild);
if(root->rightChild != NULL)
destroyTree((root->rightChild));

free(root);
root = NULL;
return ;
}

int main(void)
{
int m;

while(scanf("%d", &m) == 1)
{
if(m == 0)
break;

//构建Huffman树
struct Node* root = generateHuffman(m);

int* code = (int*)malloc(sizeof(int) * m);
memset(code, -1, sizeof(int) * m);
printHuffmanCode(root, code);
destroyTree(root);
free(code);
}

return 0;
}


结果输出如下所示:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息