贪心算法

来源:互联网 发布:人工智能李开复百度云 编辑:程序博客网 时间:2024/06/11 04:06

贪心算法和动态规划有异曲同工之秒,能用贪心法的,就不用动规。我只想说,动态规划真TM难。

  • 分数背包问题
  • Huffman编码

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

分数背包问题

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

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


输入:
          第一行是一个整数m,表示商品的数量
          第二行是m个整数,表示商品的价值

输出:
          最大价值v


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

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

商品(i) 1 2 3 体积(v) 10 20 30 价值(w) 60 100 120 性价比(p) 6 5 4

所以我们先取商品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编码

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

字符 a b c d e f 频率 45 13 12 16 9 5 变长编码 0 101 100 111 1101 1100

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

初始状态,集合里是每个结点,首先选择「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;}

结果输出如下所示:

原创粉丝点击