《算法导论》第十六章——贪心算法

  虽然写这个博客主要目的是为了给我自己做一个思路记忆录,但是如果你恰好点了进来,那么先对你说一声欢迎。我并不是什么大触,只是一个菜菜的学生,如果您发现了什么错误或者您对于某些地方有更好的意见,非常欢迎您的斧正!

贪心算法并不保证能得到最优解,但对于很多问题确实可以得到最优解。

贪心算法,如它的名字一样,十分“贪心”。如果说,动态规划是一位深谋远虑的智者,那么贪心算法就是一个目光短浅的山贼,它总是选取看起来最佳的选择,却不为以后做考虑。
比如我有重量为60,50,30,50这样4颗依次摆放的钻石,它们的价值依次为6,5,3,5,我现在书包最多能装重量为100的东西,动态规划得到的最优解就是拿两颗50的,但是贪心算法在看到60的时候就会把60放入书包,最后只能装到90。

16.1活动选择问题

简单地讲,就是有一个公用教室,很多班级要用它来搞活动,比如(1)班想下午(3)点到下午5点借用教室,(2)班想晚上7点到晚上9点借用教室,那就不会产生冲突,但是如果现在(3)班想在晚上6点晚上8点借用教室,那就会与(2)班造成冲突,两者必须有一者放弃。又比如(4)班要借用的时间段为下午5点到晚上7点。现在要选择这个教室可以给哪几个班用,使得最多的班的要求能够达到满足。那显然管理员应该优先借给(1)班、(2)班与(4)班。

现在我们的活动集为S(i是班号,si是起始时间,fi是结束时间):

在这里插入图片描述

两种安排为{1,4,8,11}、{2,4,9,11}

活动选择问题的最优子结构

c[i,j]表示集合Si,j的最优解的大小。

在这里插入图片描述

贪心选择

选择最早结束的那个活动,这样就有更多时间留给别的活动。(如果最早结束的活动有很多个,就选择任意一个)。

迭代贪心算法

感觉书中的伪代码不是很看得懂,就用白话文说一下我的理解吧。
首先我们的活动已经按照结束时间排好序了,然后我们要把第一个,也就是最早结束的活动加入到我们的安排中,从第二个活动开始遍历,只要下一个的开始时间晚于上一个的结束时间,就把它加入到我们的安排列表中。

我们用数组b来记录要加入安排的活动。这是我自己写的伪代码

b[1]=true
j=1
i=2 to n
if(s[i]≥f[j]) //下一个的开始时间晚于上一个的结束时间
b[i]=true//加入安排
j=i
else
b[i]=false

16.2贪心算法原理

步骤:
1.确定问题的最优子结构
2.设计一个递归算法
3.证明如果我们做出一个贪心选择,则只剩下一个子问题
4.证明贪心选择总是安全的
5.设计一个递归算法实现贪心策略
6.将递归算法转换为迭代算法

贪心选择性质

当进行选择时,我们直接做出在当前问题中看来最优的选择,而不必考虑子问题的解。

最优子结构

如果一个问题的最优解包含其子问题的最优解,则称此问题具有最优子结构性质。这个性质是能否应用动态规划和贪心算法的关键要素。

贪心对动态规划

0-1背包问题
假如我有一只只能装载150kg的包,现在这里有一些物品:
物品 1  2  3  4  5  6  7
重量 35  30 60   50   40 10  25   用数组w[]表示
价值 10  40 30  50  35  40  30   用数组v []表示

我们有三种策略可以选择

1、每次装的都选择性价比最高的,也就是价值/重量最大
2、每次装都选择重量最轻的
3、每次装都选择价值最高的

我们采用第一种方式。

16.3赫夫曼编码

赫夫曼编码可以很有效地压缩数据:通常可以节省20%~90%的空间,具体压缩率依赖于数据的特性。

在这里插入图片描述

来看上面一组数据,加入我们使用定长编码,则a=000,b=001…f=101,这种方法需要3x100x1000=300000个二进制位来编码文件。

假如我们采用变长编码:a=0,b=101,c=100,d=111,e=1101,f=1100,则需要(45x1+13x3+12x3+16x3+9x4+5x4)x1000=224000位。

前缀码

我们注意到上面的编码中:没有任何码字是其它码字的前缀。

将3个字符的文件abc编码为0·101·100=0101100,“·”表示连接操作。

二进制码001011101可以唯一解析为0·0·101·1101,解析码为aabe

文件的最优编码方案总是对应一棵满二叉树。

x.freq表示x的频率。

在这里插入图片描述
简单的说,就是不停地从已有的结点中找到最小的两个结点,然后连接起来。

这部分代码我不知道怎么写,应该说我太菜了,没有写成功,所以底下就只有活动选择和0-1背包问题的代码:

贪心算法_活动安排.h

#pragma once
/*活动安排*/
void ActManage(int n, int s[], int f[], int b[]);

/*测试函数*/
void TestActManage();

贪心算法_活动安排.cpp

#include "贪心算法_活动安排.h"
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

/*活动安排*/
void ActManage(int n, int s[], int f[], bool b[])
{
	b[1] = true;
	int j = 1;

	for (int i = 2; i < n; i++)
	{
		if (s[i] >= f[j])
		{
			b[i] = true;
			j = i;
		}
		else
			b[i] = false;
	}
}

/*测试函数*/
void TestActManage()
{
	int s[] = { 0,1,3,0,5,3,5,6,8,8,2,12 };/*12*/
	int f[] = { 0,4,5,6,7,9,9,10,11,12,15,16 };

	int n = sizeof(s) / sizeof(s[0]);

	bool* b = new bool[n];
	ActManage(n, s, f, b);
	
	for (int i = 1; i < n; i++)
	{
		if (b[i])cout << "活动" << i << "开始于" << s[i] << ",结束于" << f[i] << endl;
	}
	delete[] b;
}

主函数

#include "贪心算法_活动安排.h"
#include <stdio.h>

int main()
{
	TestActManage();
	getchar(); 
	getchar(); 
	return 0;
}

运行结果

在这里插入图片描述

贪心算法_01背包问题.h

#pragma once

/*装载方法*/
void BackageLoad(float cost[], bool b[], int w[], int weight, int n);

/*测试函数*/
void TestBackage();

贪心算法_01背包问题.cpp

#include "贪心算法_01背包问题.h"
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

/*装载方法*/
void BackageLoad(float cost[], bool b[], int w[], int weight, int n)
{
	int temp;
	bool flag = false;/*判断是否还能再挑出物品*/

	while (weight >= 0)/*当袋子还能装下的时候*/
	{
		float max = 0;
		for (int i = 0; i < n; i++)
		{
			if (!b[i] && cost[i] > max&&weight - w[i] >= 0)/*如果b[i]还没有装进去*/
			{
				max = cost[i];
				temp = i;
				flag = true;
			}
		}
		if (!flag)
			break;
		weight -= w[temp];
		b[temp] = true;
	}
}

/*测试函数*/
void TestBackage()
{
	int w[7] = { 35,30,60,50,40,10,25 };/*重量*/
	int v[7] = { 10,40,30,50,35,40,30 };/*价值*/
	float cost[7];/*性价比*/
	bool b[7];/*记录是否装进袋子里*/
	int weight = 150;/*袋子承重*/
	int i;

	for (i = 0; i < 7; i++)
	{
		cost[i] = (float)v[i] / (float)w[i];
		b[i] = false;
	}

	BackageLoad(cost, b, w, weight, 7);

	for (i = 0; i < 7; i++)
	{
		if (b[i])
		{
			cout << "选择物品" << i+1 << ",它重" << w[i] << ",价值为" << v[i] << endl;
		}
	}
}

主函数

#include "贪心算法_01背包问题.h"
#include <stdio.h>

int main()
{
	TestBackage();
	getchar(); 
	getchar(); 
	return 0;
}

运行结果

在这里插入图片描述

参考网站及博客

曹磊的博客
https://www.cnblogs.com/cao-lei/p/6896841.html

猜你喜欢

转载自blog.csdn.net/weixin_40851250/article/details/83688309
今日推荐