1、掌握贪心算法的基本思想。
2、学习利用贪心算法设计和实现算法的方法。
3、了解利用替换法证明贪心策略是否能获得全局最优解的过程。
4、熟练掌握贪心算法在两个典型图搜索中的应用,即单源最短路径和最小生成树算法中,利用合理的数据结构优化算法复杂度的技巧。
1、问题描述:利用贪心法来设计并实现最优装载问题
2、问题描述:利用贪心法来设计并实现单源最短路径。
3、问题描述:字符a~h出现的频率恰好是前n个Fibonacci数,它们的哈夫曼编码是什么?扩展到前n个字符的频率恰好是前n个Fibonacci数的情形。写程序实现。
问题描述:有一批集装箱要装上一艘载重量为C的轮船。其中集装箱i的重量为Wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
#include
int main(){
int n = 0;
int max_weight;
scanf("%d",&max_weight); //输入轮船的载重量
scanf("%d",&n); //输入集装箱的数量
int a[n] = {0};
for(int i = 0;i < n; i++){ //将每个集装箱的重量存入数组a[n]中
scanf("%d",&a[i]);
}
for(int i= 0;i < n; i++){ //用冒泡排序将货物从小到大排列
for(int j = 0;j < i;j++){
if(a[j]>a[j+1]){
int temp = 0;
temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
int b[n] = {0}; //用数组b[n]记录能装入轮船的集装箱
for(int i = 0;i < n;i++){
max_weight = max_weight - a[i];
if(max_weight > 0){ //还大于零就代表轮船还能继续装集装箱
b[i] = a[i]; //装进一个,数组b就记录一个
}
}
for(int i = 0;i < n; i++){
if(b[i] != 0){
printf("%d ",b[i]);
}
}
}
//【贪心算法】单源最短路径问题
#include
using namespace std;
#define N 5 // 5个顶点,1、2、3、4、5
#define M 9999 // maxint,大整数
void Dijkstra(int n, int v, int dist[], int prev[], int c[][N + 1]);
void Traceback(int v, int i, int prev[]);
int main()
{
int v = 1; // 源点为1
int dist[N + 1]; // 从源到顶点i的最短特殊路径长度
int prev[N + 1]; // 从源到顶点i的最短路径上前一个顶点
// 带权有向图的邻接矩阵,行和列下标从1开始
int c[N + 1][N + 1] = {
{M, M, M, M, M, M },
{M, M, 10, M, 30, 100 },
{M, M, M, 50, M, M },
{M, M, M, M, M, 10 },
{M, M, M, 20, M, 60 },
{M, M, M, M, M, M },
};
// “输入”:带权有向图
cout << "带权有向图的邻接矩阵为:\n";
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= N; j++)
cout << c[i][j] << "\t";
cout << endl;
}
// Dijkstra算法
Dijkstra(N, v, dist, prev, c);
// 输出
cout << "源点1到顶点5的最短路径长度为:" << dist[5];
cout << ",路径为:";
Traceback(1, 5, prev);
cout << endl;
return 0;
}
void Dijkstra(int n, int v, int dist[], int prev[], int c[][N + 1])
{
bool s[N + 1]; // 顶点集合s
for (int i = 1; i <= n; i++)
{
dist[i] = c[v][i]; // 从源到顶点i的最短特殊路径长度
s[i] = false;
if (dist[i] == M)
prev[i] = 0; // 从源到顶点i的最短路径上前一个顶点
else
prev[i] = v;
}
dist[v] = 0;
s[v] = true;
for (int i = 1; i < n; i++)
{
int temp = M; //
int u = v; // 上一顶点
// 找到具有最短特殊路长度的顶点u
for (int j = 1; j <= n; j++)
{
if ((!s[j]) && (dist[j] < temp))
{
u = j;
temp = dist[j];
}
}
s[u] = true;
// 更新dist值
for (int j = 1; j <= n; j++)
{
if ((!s[j]) && (c[u][j] < M))
{
int newdist = dist[u] + c[u][j];
if (newdist < dist[j])
{
dist[j] = newdist;
prev[j] = u;
}
}
}
}
}
//输出最短路径 v源点,i终点,
void Traceback(int v, int i, int prev[])
{
// 源点等于终点时,即找出全部路径
if (v == i)
{
cout << i;
return;
}
Traceback(v, prev[i], prev);
cout << "->" << i;
}
问题描述:字符a~h出现的频率恰好是前n个Fibonacci数,它们的哈夫曼编码是什么?扩展到前n个字符的频率恰好是前n个Fibonacci数的情形。写程序实现。
/*字符a~h出现的频率恰好是前n个Fibonacci数,它们的哈夫曼编码是什么?
扩展到前n个字符的频率恰好是前n个Fibonacci数的情形。写程序实现。*/
#include
#include
#include
#include
int fib(int i){ /*斐波那契函数改进,此函数能得到第n个数的斐波那契函数的值*/
int j,a,b,c;
int shuzu[i]={0};
a=1;
b=1; /* 前两项已知为1,直接输出 */
if(i<3){
return 1;
}
else{
for(j=2;jbody=(struct HTreeNode *)malloc(sizeof(struct HTreeNode)*size);
ht->length=0;
int i;
for(i=0;ibody[i].parent=-1;
ht->body[i].lchild=-1;
ht->body[i].rchild=-1;
int temp;
temp=fib(i+1);
printf("The %dth weight= %d \n",i,fib(i+1));
ht->body[i].weight=temp;
ht->length++;
}
for(;ibody[i].parent=-1;
ht->body[i].lchild=-1;
ht->body[i].rchild=-1;
ht->body[i].weight=pow(2,31)-1;
}
return ht;
}
void show(struct HTree *ht){/*打印哈夫曼树*/
if(ht->body!=NULL){
for(int i=0;ilength;i++){
printf("%4d",ht->body[i].lchild);
printf("%4d",ht->body[i].weight);
printf("%4d",ht->body[i].rchild);
printf("%4d",ht->body[i].parent);
printf("\n");
}
}
}
void findTwoMinHTNode(struct HTree *ht,int *min1,int *min2){
int m1,m2;
int minWeight;
int j=0;
while(ht->body[j].parent!=-1){
j++;
}
m1=j;
minWeight=ht->body[m1].weight;
for(int i=m1+1;ilength;i++){
if(ht->body[i].parent==-1&&ht->body[i].weightbody[i].weight;
}
}
ht->body[m1].parent=1;
*min1=m1;
j=0;
while(ht->body[j].parent!=-1){
j++;
}
m2=j;
minWeight=ht->body[m2].weight;
for(int i=m2+1;ilength;i++){
if(ht->body[i].parent==-1&&ht->body[i].weightbody[m2].weight;
}
}
*min2=m2;
ht->body[m2].parent=1;
}
void consummateHT(struct HTree *ht,int num){/*完善哈夫曼树*/
if(ht==NULL){
printf("HT is not exit\n");
return;
}
int min1,min2;
for(int i=num;i<2*num-1;i++){
findTwoMinHTNode(ht,&min1,&min2);
ht->body[min1].parent=i;
ht->body[min2].parent=i;
ht->body[i].lchild=min1;
ht->body[i].rchild=min2;
ht->body[i].weight=ht->body[min1].weight+ht->body[min2].weight;
ht->length++;
}
}
void codeHT(struct HTree *ht){/*哈夫曼编码核心代码*/
int count=(ht->length+1)/2;
int weightdata[count];
for(int i=0;ibody[i].weight;
}
char *ch[count];
char a[count];
int index=0;
for(int i=0;ilength;i++){
ht->body[i].weight=0;
}
int cur=ht->length-1;
while(cur!=-1){
if(ht->body[cur].weight==0){/*weight为0表示没有被访问过*/
ht->body[cur].weight=1;/*weight为1表示左子树被访问过*/
if(ht->body[cur].lchild!=-1){/*左孩子不等于-1说明还在中间节点,cur继续往左走,编码左0右1,所以编为0*/
a[index++]='0';
cur=ht->body[cur].lchild;
}else{/*当前节点的左孩子为-1说明遍历到头了,当前节点的完整编码就存在数组a中*/
a[index]='\0';
ch[cur]=(char *)malloc(sizeof(char)*count);
strcpy(ch[cur],a);/*对编码拷贝*/
}
}else if(ht->body[cur].weight==1){/*weight为1表示右子树没被访问过*/
ht->body[cur].weight=2;/*weight为2表示左右子树都被访问过*/
if(ht->body[cur].rchild!=-1){/*右孩子孩子不等于-1说明还在中间节点,cur继续往右走,编码左0右1,所以编为1*/
a[index++]='1';
cur=ht->body[cur].rchild;
}
/*右孩子为-1时说明遍历到头了,由于之前编码已经拷贝过了,所以直接跳过,不做处理*/
}else{/*weight为2说明左右都访问过,回退到父节点,并将索引下标减一,相当于删掉数组中最后一个字符*/
ht->body[cur].weight=0;
cur=ht->body[cur].parent;
index--;
}
}
printf("最优前缀码:\n");
for(int i=0;i
注:其中的关于哈夫曼编码的代码参考了此博客 哈夫曼编码代码实现(C语言)
自己写!!!