算法设计初步之贪心算法

简介: 算法设计初步之贪心算法

理论知识


贪心算法是指是这样一种方法,分步骤实施,它在每一步仅作出当时看起来最佳的选择,即局部最优的选择,希望这样的选择能导致全局最优解。在贪心算法的每一步所做的局部最优选择就叫做贪心选择。

其中贪心算法中贪心选择性质和最优子结构性是两个关键要素!

贪心选择性质:即是可以通过贪心选择来构造全局最优解

最优子结构性质上文已述。

贪心求解一般有下面几个步骤

做出贪心选择,用反证法假设当前步骤不是最优,推出矛盾,得到其是最优;再用数学归纳法证明其全局最优。

这里证明比较复杂,算是一个难点。


动态规划和贪心算法都能解决一些最优性问题,那么二者异同在哪?

基本上能用贪心也就能用动态规划,二者应用的问题都具有最优子结构,如果说动态规划是在所有子问题最优解的基础上推出更大的子问题的最优解直到得到答案,那么贪心就省略了所有子问题这个过程,它是做出一个选着再求解剩下的那个子问题,自顶向下。


一、活动选择


问题描述

假定有一个n个活动的集合S={a1,a2,…,an},这些活动都要求使用同一资源(如演讲会场),而这个资源在某个时刻只能供一个活动使用。每个活动ai都有一个开始时间si和一个结束时间fi,且0≤si


这个问题是典型的区间贪心问题

贪心选择:如果我们把各个活动按照结束时间排序,要求最大兼容集合,我们总是想着在兼容条件内选择最早结束的时间

证明:略

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000;
 struct node{
  int x,y; //定义活动结构体,x,y分别代表起始时间 
  bool operator <(const node &a)const{
  return y<a.y;
  }
 }a[maxn];
int main(){
  int n;scanf("%d",&n);
  for(int i=0;i<n;i++)
  scanf("%d%d",&a[i].x,&a[i].y);
  sort(a,a+n) ;//将其结束时间排序
  int sum=0,end;
  if(n) {  
  sum=1;end=a[0].y; //第一次贪心选择 
  }
  for(int i=1;i<n;i++) //贪心选择到结束 
  if(a[i].x>=end){
  sum++;
  end=a[i].y;
  }
  printf("%d",sum);
  return 0;
}


一个结果:

1685018274338.jpg


二、Huffman编码


数据结构里老生常谈的问题

贪心选择:每次选择待选集合中最小两个做叶子节点,其和为根结点加入待选集合。


代码思路:有n个结点,最后和就n-1个,可以用一个2n-1大的结构数组来构造Huffman树,用一个优先队列来存放集合

#include<iostream> 
#include<queue>
#include<string>
using namespace std;
#define N 1000
struct node{   //哈夫曼树的结构,采用静态二叉树 
  int weight;
  int parent;
  int lchild,rchild; 
} Node[N]; //定义一个足够大的静态哈夫曼树 
struct cmp{   //将优先队列按从小到大排列的比较函数 
  bool operator ()(int a,int b){
  return Node[a].weight>Node[b].weight; 
  }
};
priority_queue<int,vector<int>,cmp>q;
//弄成int型方便修改Node里的值 
string s[100];
void createhuff(int n){  //创建哈夫曼树 
  for(int i=1;i<=n;i++)
q.push(i);
int i,j,m=n+1;
while(q.size()>1){
  i=q.top();q.pop();
  j=q.top();q.pop();
  Node[m].weight=Node[i].weight+Node[j].weight;
  Node[m].lchild=i;Node[m].rchild=j;
  Node[i].parent=Node[j].parent=m;
  q.push(m++);
} 
}
void preorder(int root){  //前序遍历 
  if(root==0)
  return;
  printf("%d ",Node[root].weight);
  preorder(Node[root].lchild);
  preorder(Node[root].rchild);
}
void encodehuff (int n) {//生成编码,从下往上 
  for(int i=1;i<=n;i++){
  int p=Node[i].parent,k=i;
  string str;
  while(p){
    if(Node[p].lchild==k){ //这里生成的是逆向代码 
    str+="1";}
    else {
    str+="0";}  
    k=p;
    p=Node[k].parent;
  }
  for(int f=str.length();f>0;f--) //将其编码正向 
  s[i]+=str[f-1];
  }
}
void  printfcode(int n) {
  for(int i=1;i<=n;i++) 
  printf("%s\n",s[i].c_str());
  //这里用printf输出字符串需要将它转化为字符数组,用cout不用 
}
int main(){
  int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){  //从1开始,0值与空对应 
  scanf("%d",&Node[i].weight);
  Node[i].parent=Node[i].lchild=Node[i].rchild=0;
}
createhuff(n);
encodehuff(n);
printfcode(n);
return 0;
}


此外还有最小生成树问题、Dijkstra 算法等也应用了贪心算法稍后再论。


相关文章
|
7月前
|
算法 数据挖掘 调度
【算法分析与设计】贪心算法(下)
【算法分析与设计】贪心算法(下)
【算法分析与设计】贪心算法(下)
|
移动开发 算法 调度
【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)
贪心算法是一种非常常见的算法,它的简单和高效性使其在实际应用中被广泛使用。 贪心算法的核心思想是在每一步都采取当前状态下最优的选择,而不考虑未来可能产生的影响。虽然贪心算法不能保证总是得到最优解,但在很多情况下,它可以获得很好的结果。 本篇文章将介绍贪心算法的基本概念和一些经典应用,以及如何通过贪心算法来解决一些实际问题。希望通过本文的阅读,读者可以对贪心算法有更加深刻的理解,并能够在实际问题中应用贪心算法来得到更好的解决方案。 让我们暴打贪心算法吧!
1484 0
|
2月前
|
人工智能 算法 Java
【算法设计与分析】— —单源最短路径的贪心算法
【算法设计与分析】— —单源最短路径的贪心算法
32 0
|
6月前
|
算法
算法怎么算:贪心算法
注意:这里是期望最优,而非必定最优。也就是说存在期望落空的情况。而在这种情况下,贪心算法,并非最优解。
29 0
|
7月前
|
存储 人工智能 自然语言处理
动态规划算法总结
动态规划算法总结
|
7月前
|
机器学习/深度学习 存储 算法
【算法分析与设计】贪心算法(上)
【算法分析与设计】贪心算法(上)
|
10月前
|
存储 算法 搜索推荐
动态规划算法
动态规划算法是一种常用的优化问题求解方法,主要用于解决具有重叠子问题和最优子结构性质的问题。动态规划算法的基本思想是将原问题拆分成若干个子问题,通过求解子问题的最优解来求解原问题的最优解。动态规划算法通常包含以下三个步骤:
72 2
|
10月前
|
机器学习/深度学习 自然语言处理
动态规划算法(一)
动态规划算法
67 0
|
10月前
|
机器学习/深度学习 人工智能 JavaScript
动态规划算法(二)
动态规划算法
45 0
|
12月前
|
算法
算法设计初步之动态规划
算法设计初步之动态规划