贪心算法——找零钱、装豆子、分糖果、区间覆盖、霍夫曼编码等

一、找零钱

根据实际情况,有1元、(2元的已经不流通了)、5元、10元、20元、50元、100元。当有人给出36524元时,如何找钱给对方。

// 找钱的类
class payMoney
{
public:
	payMoney();
	~payMoney();
	int howManyCash(int waitToPay);
private:
	int *arr;
};
// 初始化钱的种类
payMoney::payMoney()
{
	arr= new int[6];
	arr[0] = 100;
	arr[1] = 50;
	arr[2] = 20;
	arr[3] = 10;
	arr[4] = 5;
	arr[5] = 1;
}
// 析构
payMoney::~payMoney()
{
	delete [] arr;
}
// 找钱函数
// inparam: 等待找钱
// outparam: 总共需要多少张钱
int payMoney::howManyCash(int waitToPay)
{
	int count = 0;        // 用于记录总共需要钱的数量
	int num = 0;
	for (int i = 0; i < 6; ++i)
	{
		num = waitToPay/arr[i];        // 优先找面值较大的钱,需要的张数
		if (num >= 1)
		{
			count += num;
			cout << "need " << num << " " << arr[i] << "RMB" << endl;
			waitToPay -= num * arr[i];
		}
	}
	cout << "total need " << count <<" RMB." << endl;
	return count;
}

int main()
{
	// payRMN:
	payMoney pmoney;
	cout << "please input need to pay monay: " << endl;
	int money;
	cin >> money;
	if (money > 65535)
	{
		cout << "Illegal input!!!"<<endl;
	}
	else
	{
		pmoney.howManyCash(money);
	}
    return 0;
}

 注意:如果对于有币种:100,99,1。当需要找396元时,贪心的结果是需要3张100,99张1元。但实际上,只需要4张99元,即可。

二、装豆子

#include<iostream>
using namespace std;

void swap(int &a, int &b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}

void swap(char &c1, char &c2)
{
	char temp;
	temp = c1;
	c1 = c2;
	c2 = temp;
}

void compute(int *wei, int *total, int *single, int len)
{
	for(int i = 0; i < len; ++i)
	{
		single[i] = total[i]/wei[i];
	}
}

void sortSmallBig(int *wei, int *total, int *single, char *b, int len)
{
	bool flag = true;
	for(int i = 0; i < len - 1; ++i)
	{
		flag = true;
		for(int j = 0; j < len - i - 1; ++j)
		{
			if(single[j] > single[j + 1])
			{
				swap(single[j], single[j + 1]);
				swap(wei[j], wei[j + 1]);
				swap(total[j], total[j + 1]);
				swap(b[j], b[j + 1]);
				flag = false;
			}
		}
		if(flag)
			break;
	}
}

void print(int *arr, int len)
{
	for(int i = 0; i < len; ++i)
	{
		cout << arr[i] << ", ";
	}
	cout << endl;
}

void print(char *c, int len)
{
	for(int i = 0; i < len; ++i)
        {
                cout << c[i] << ", ";
        }
        cout << endl;
}

void putInBag(int *wei, int *single, int len, char *b, int need)
{
	while(need > 0)
	{
		for(int i = len - 1; i >= 0; --i)
		{
			if(need >= wei[i])
			{
				need -= wei[i];
				cout << "need " << b[i] << " bean" << wei[i] << " kg" << endl;
			}
			else if(need > 0)
			{
				cout << "need " << b[i] << " bean" << need << " kg" << endl;
				need = 0;
			}
			else
				break;
		}
	}
}

int main()
{
	int weight[5] = {100, 30, 60, 20, 50};
	int totalVal[5] = {100, 90, 120, 80, 75};
	int singleVal[5] = {0,0,0,0,0};
	char bean[5] = {'y', 'g', 'r', 'b', 'q'};
	compute(weight, totalVal, singleVal, 5);		// 计算单价
	sortSmallBig(weight, totalVal, singleVal, bean, 5);	// 排序
	cout << "输出排序后的重量: " << endl;
	print(weight, 5);
	cout << "输出排序后的总价: " << endl;
	print(totalVal, 5);
	cout << "输出排序后的单价: " << endl;
	print(singleVal, 5);
	cout << "输出豆子种类:b-黑豆,g-绿豆,q-青豆,r-红豆,y-黄豆"<< endl;
	print(bean, 5);
	cout << "输出需要的哪种豆子,多少千克。" << endl;
	putInBag(weight, singleVal, 5, bean, 100);
	return 0;
}

 

三、分糖果

把糖果大小和孩子的需求,从小到大排序,优先满足需求小的孩子。由于满足一个需求大的孩子和满足一个需求小的孩子,对最终的期望值贡献是一样的,所以也可以都按照从大到小开始匹配。

以下例子从小到大开始匹配。

// 分糖果的类
class candyDistribute
{
public:
	candyDistribute();
	~candyDistribute();
	void distribute(int *kid, int kidnum, int *candy, int candynum);
	void print(int *arr, int len);
private:
	void sortSmallBig(int *arr, int len);
};

candyDistribute::candyDistribute()
{
}

candyDistribute::~candyDistribute()
{
}
// 把孩子的需求和糖果的大小按照从小到大的顺序排列
void candyDistribute::sortSmallBig(int *arr, int len)
{
	int preIndex, current;
	for (int i = 1; i < len; ++i)
	{
		preIndex = i - 1;
		current = arr[i];
		while(preIndex >= 0 && arr[preIndex] > current)
		{
			arr[preIndex +1] = arr[preIndex];
			--preIndex;
		}
		arr[preIndex + 1] = current;
	}
}
// 打印输出,用于测试。
void candyDistribute::print(int *arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
// inparam: 孩子的数组,和糖果的数组
void candyDistribute::distribute(int *kid, int kidnum, int *candy, int candynum)
{
	sortSmallBig(kid, kidnum);
	sortSmallBig(candy, candynum);
	int kidcurrent = 0;          // 当前第几个孩子
	int candycurrent = 0;         // 当前第几个糖果
	while (kidcurrent < kidnum && candycurrent < candynum)        // 糖果和孩子均没有匹配完
	{
		if (kid[kidcurrent] <= candy[candycurrent])
		{
			cout << " kid " << kid[kidcurrent] << " match " << " candy " << candy[candycurrent] << endl;
			++kidcurrent;
			++candycurrent;
		}
		else
		{
			++candycurrent;
		}
	}
	if (kidcurrent == kidnum)
	{
		cout << "perfect! every kid own a candy." << endl;
	}
	else
	{
		cout << "just satisfy " << kidcurrent << " kids!" << endl; 
	}
}

int main()
{
    candyDistribute candyD;
	int kid[] = {5,10,2,9,15,9,10};
	int candy[] = {6,1,20,3,8,12,18};

	cout << "kid need: "<< endl;            // 排序前孩子的需求
	candyD.print(kid, 7);

	cout << "canny weight:" <<endl;          // 排序前糖果的大小
	candyD.print(candy,7);

	cout << "kid match candy;" << endl;       // 孩子需求和糖果匹配
	candyD.distribute(kid,7,candy,6);

	cout << "kid need sort : "<< endl;        // 输出,查看排序是否正确
	candyD.print(kid, 7);

	cout << "canny weight sort :" <<endl;      // 输出,查看排序是否正确
	candyD.print(candy,7);
    return 0;
}

四、区间覆盖

 

4.1 以第一个数据为起始点,找出不连续的一组数据。代码如下: 

#include<iostream>
using namespace std;
// 交换函数,用于排序
void swap(int &a, int &b)
{
	int temp = 0;
	temp = a;
	a = b;
	b = temp;
}

// 把区间数据,按照开始位置,由小到大排序
// 两个数组长度相同
void sortSmallBig(int *arrmin, int *arrmax, int len)
{
	bool flag = true;
	for(int i = 0; i < len - 1; ++i)
	{
		flag = true;
		for(int j = 0; j < len - i - 1; ++j)
		{
			if(arrmin[j] > arrmin[j + 1])
			{
				swap(arrmin[j], arrmin[j + 1]);        // 交换起始点
				swap(arrmax[j], arrmax[j + 1]);        // 交换结束点
				flag = false;
			}
		}
		if(flag)
			break;
	}
}
// 查找区域不想交的点,然后标记索引
void Coverage(int *arrmin, int *arrmax, int len, char *c)
{
	sortSmallBig(arrmin, arrmax, len);
	c[0] = '1';
	int index = 0;
	for(int i = 1; i < len; ++i)
	{
		if(arrmin[i] >= arrmax[index])
		{
			c[i] = '1';
			index = i;
		}
	}
}
// 输出所有不想交的区间
void print(int *arrmin, int *arrmax, char *c, int len)
{
	for(int i = 0; i < len; ++i)
	{
		if(c[i]=='1')
		{
			cout << "start: " << arrmin[i] << "  end: " << arrmax[i] << endl;
		}
	}
}
// 测试
int main()
{
	int Imin[6] = {0,2,3,1,5,8};
	int Imax[6] = {3,4,5,5,9,10};
	char c[6] = {'0','0','0','0','0','0'};        // 用于记录被选中的区间的索引
	Coverage(Imin, Imax, 6, c);
	print(Imin, Imax, c, 6);
	return 0;
}

4.2 查找所有区间不想交的点。 

链接如下:https://github.com/hitskyer/course/blob/master/dataAlgorithm/dongyaxing/Greedy_file/IntervalCoverage.cpp

五、霍夫曼编码

 

 

 

 

#include <iostream>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <string>
#include <algorithm>
#include <memory.h>
#define N 6
typedef unsigned int UINT;
using namespace std;

typedef struct HFMnode
{
	char data;
	char code;
	UINT weight;
	HFMnode *parent, *lp, *rp;
	HFMnode()
	{
		data = '/';
		code = '0';
		weight = 0;
		parent = lp = rp = NULL;
	}
}HFMnode;

class comp
{
public:
	bool operator()(HFMnode* &a, HFMnode* &b)const
	{
		if (a->weight == b->weight)
		{
			return a->data > b->data;
		}
		return a->weight > b->weight;
	}
};

class HuffmanTree
{
public:
	HFMnode *root;		// 根结点指针
	HFMnode *node[2*N-1];	// N个字符,霍夫曼树结点个数2*n-1
	priority_queue<HFMnode*, vector<HFMnode*>, comp> pri_queue;	// 插入的同时,进行排序。按照权重从大到小
	// 优先队列中存放类指针时,第三个参数应该另写一个comp类,类内写operator()
	void creatTree_outputCode(int *w)
	{
		char ch = 'a';
		HFMnode *left, *right;
		for (int i = 0; i < N; ++i, ++ch)		// 把abcdef存入,且权重为输入对象数组。
		{
			// 生成钱N个字符节点,输入权重和字符信息
			node[i] = new HFMnode();
			node[i]->weight = w[i];
			node[i]->data = ch;
			pri_queue.push(node[i]);
		}

		for (int i = N; i < 2*N-1; ++i)
		{
			// 后面新生成的N-1个节点
			node[i] = new HFMnode();
			if (pri_queue.top()->data != '/')
			{
				//队首节点不是新生成的,队首放右边
				right = pri_queue.top();
				right->code = '1';
				pri_queue.pop();
				left = pri_queue.top();
				left->code = '0';
				pri_queue.pop();
			}
			else
			{
				//队首节点是新生成的,队首放左边
				left = pri_queue.top();
				left->code = '0';
				pri_queue.pop();
				right = pri_queue.top();
				right->code = '1';
				pri_queue.pop();
			}
			// 新节点权值、上下连接指针对接
			node[i]->weight = left->weight + right->weight;
			node[i]->lp = left;
			node[i]->rp = right;
			left->parent = node[i];
			right->parent = node[i];
			pri_queue.push(node[i]);	// 新生成的结点cherub队列
		}
		root = pri_queue.top();		// 最后还剩一个结点,是根节点
		creatHuffCode();
		for (int i = 0; i < 2*N-1; ++i)
		{
			delete node[i];
		}
	}

	void creatHuffCode()
	{
		HFMnode* parent;
		string huffCode;	// 霍夫曼编码
		int codelen = 0;	// 输入的字符串编码后的总长度bits
		for (int i = 0; i < N; ++i)
		{
			huffCode = "";
			parent = node[i];	// 从自己(叶子节点)开始向上找父结点,直到root
			cout << i + 1 << " " << node[i]->data << " The Huffman encoding is : ";
			while(parent != root)
			{
				//huffCode.push_back(parent->code);	// 将路径中的编码汇成字符串
				huffCode+=parent->code;
				parent = parent->parent;
			}
			reverse(huffCode.begin(), huffCode.end());	// 将最终的编码翻转一下
			cout << huffCode << endl;
			codelen += huffCode.size()*node[i]->weight;		// 单字节code长*出现的次数
		}
		cout << "The Huffman encoding length of the string is " << codelen << " bits." << endl;
	}
};

int main()
{
	HuffmanTree huff;
	cout << "Please enter the weight of the character , total " << N << " character: " << endl;
	int w[] = new int[N];
	for (int i = 0; i < N; ++i)
	{
		cout << "No. " << i+1 << " ";
		cin >> w[i];
	}
	huff.creatTree_outputCode(w);
//	system("pause");
	return 0;
}

六、Prim和Kruskal最小生成树

Prim 最小生成树的代码链接(C++代码):

https://github.com/hitskyer/course/blob/master/dataAlgorithm/dongyaxing/Graph_file/MiniSpanTree_prim.cpp

Kruskal最小生成树代码链接(C++代码):

https://github.com/hitskyer/course/blob/master/dataAlgorithm/dongyaxing/Graph_file/MiniSpanTree_Krushal.cpp

七、Dijkstra单源最短路径

未完待续……

八、移除K个数,使得最终数字最小

abcdef.....

从高位a开始,与左边低一位的b数字进行比较,a > b,则移除a.。否则,继续用b 与左边低一位c进行比较。

4556847594546->remove 8 -> 455647594546 -> remove 6 -> 45547594546 -> remove second 5 -> 4547594546

-> remove 5-> 447594546......

九、排队的n个人,等待被服务,只有一个服务员。每个人服务时间不等,如何n个人总的等待时长最小。

由处理时间最短的开始服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值