贪心算法——(1)

来源:互联网 发布:java行业发展前景 编辑:程序博客网 时间:2024/05/29 19:40

今天我们来说说贪心算法,那什么是贪心算法呢?来看下定义 。

1.什么是贪心算法:

贪心算法(又称贪婪算法,Greedy Algorithm)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

2.基本思路

(1)把求解的问题分成若干个子问题。(2)对每一子问题求解,得到子问题的局部最优解。(3)把子问题的解局部最优解合成原来解问题的一个解。

3、算法实现

(1)从问题的某个初始解出发。(2)采用循环语句,当可以向求解目标前进一步时,就根据局部最优策   略,得到一个部分解,缩小问题的范围或规模。 (3)将所有部分解综合起来,得到问题的最终解。

4、贪心方法的基本思想

 *贪心是一种解题策略,也是一种解题思想。 *使用贪心方法需要注意局部最优与全局最优的关系,选择当前状态的局部最优并不一定能推导出问题的全局最优 *利用贪心策略解题,需要解决两个问题: —**该题是否适合于用贪心策略求解** —**如何选择贪心标准,以得到问题的最优解**

5、贪心法的特点

  1.贪心选择性质:算法中每一步选择都是当前看似最佳的选择,这种选择依赖于已做出的选择,但不依赖于未做的选择。  2.最优子结构性质:算法中每一次都取得了最优解(即局部最优解),要保证最后的结果最优,则必须满足全局最优解包含局部最优解。  3.但并不是所有具有最优子结构的问题都可以用贪心策略求解。因为贪心往往是盲目的,需要使用更理性的方法——动态规划(例如“0-1背包问题”与“部分背包问题”) ***

【问题1】部分背包问题


 给定一个最大载重量为M的卡车和N种食品,有食盐,白糖,大米等(假设它们都是散装且大货车只受重量限制不受体积限制)。已知第i种食品的最多拥有Wi公斤,其商品价值为Vi元/公斤,编程确定一个装货方案,使得装入卡车中的所有物品总价值最大。 **分析**:因为每一个物品都可以分割成单位块,单位块的利益越大,显然总收益越大,所以它局部最优满足全局最优,可以用贪心法解答。 **方法如下**:先将单位块收益按从大到小进行排列,然后用循环从单位块收益最大的取起,直到不能取为止便得到了最优解。 ***代码如下***
#include<cstdio>#include <iostream>#include<cstring>#include<algorithm>#include<string>#define OBJECT_NUM 3   //物品的个数#define KNAP_WEIGHT 50  //背包的重量using namespace std;struct input_object{    int w;    int v;    double r;};/*调用sort排序函数,按照价值与重量比排序,如果贪心比值相等则按照重量排序*/bool bigger(input_object a,input_object b){    if(a.r==b.r)return a.w<b.w;    else return a.r>b.r;}int main(void){    input_object *object;    int curr_w=0;//当前背包的总重量    double value=0;//背包当前的总价值    //分配空间    object=new input_object[OBJECT_NUM];    //输入各物品    for(int i=0; i<OBJECT_NUM; i++)    {        cin>>object[i].w>>object[i].v;        object[i].r=(double)object[i].v/object[i].w;//价值和重量之比    }    //对各个物品重新排序    sort(object,object+OBJECT_NUM,bigger);    for(int j=0; j<OBJECT_NUM; j++)    {        for(int k=1; k<=object[j].w; k++)        {            //每次只增加一个重量单位            if(curr_w+1<=KNAP_WEIGHT)            {                curr_w+=1;                value+=object[j].r;            }        }    }    printf("%d",value);    printf("\n");    return 0;}

【问题2】0/1背包问题


给定一个最大载重量为M的卡车和N种动物。已知第i种动物的重量为Wi,其最大价值为Vi,设定M,Wi,Vi均为整数,编程确定一个装货方案,使得装入卡车中的所有动物总价值最大。

分析:按贪心法:每次选价格最大的装载。很明显有反例:设N=3,卡车最大载重量是100,三种动物A、B、C的重量分别是40,50,70,其对应的总价值分别是80、100、150。

分析图样

代码如下

#include <iostream>#define OBJECT_NUM 3   //物品的个数#define KNAP_WEIGHT 5 //背包的重量using namespace std;//物品包括价值和重量struct input_object{    int value;    int weight;};//比较两者最大一个int bigger(int a,int b){    if(a>b)return a;    else return b;}//获取最大的价值//may_result存放可行解int get_max_value(input_object*object,int *may_result){    int max_value;    int value[OBJECT_NUM+1][KNAP_WEIGHT+1];//value[i][j]把i个物体装入容量j的背包获得的最大价值    /*把前面i个物品装入容量为0的背包    和把0个物品装入容量为j的背包,    得到的价值均为0    */    for(int i=0; i<=OBJECT_NUM; i++)    {        value[i][0]=0;//初始化第零列    }    for(int j=0; j<=KNAP_WEIGHT; j++)    {        value[0][j]=0;//初始化第零列    }    /*    计算value的值    在这里要注意object[]数组范围为0-OBJECT_NUM-1,下面的i-1表示第i个物品    */    for(int i=1; i<=OBJECT_NUM; i++)    {        for(int j=1; j<=KNAP_WEIGHT; j++)        {            if(object[i-1].weight>j)            {                value[i][j]=value[i-1][j];                //输出                cout<<value[i][j]<<" ";            }            else            {                value[i][j]=bigger(value[i-1][j],(value[i-1][j-object[i-1].weight]+object[i-1].value));                cout<<value[i][j]<<" ";            }        }        cout<<endl;    }    //逆推求解,这一步是求出放入哪些物品    int knapweight=KNAP_WEIGHT;    for(int i=OBJECT_NUM; i>0; i--)    {        if(value[i][knapweight]>value[i-1][knapweight])        {            may_result[i-1]=1;//第i个物品可以放入,may_result从0开始            knapweight=knapweight-object[i-1].weight;//减去第i个物品的重量        }        else        {            may_result[i-1]=0;        }    }    max_value=value[OBJECT_NUM][KNAP_WEIGHT];    return max_value;}int main(void){    input_object*object;    int maxValue,*mayResult;    object=new input_object[OBJECT_NUM];    mayResult=new int[OBJECT_NUM];    //输入物品    for(int i=0; i<OBJECT_NUM; i++)    {        cin>>object[i].weight>>object[i].value;    }    maxValue=get_max_value(object,mayResult);    cout <<endl<< "最大价值为:"<<maxValue << endl<<endl;    //输出放入物品的状态    cout<<"各个物品的状态为(0表示不放入,1表示放入):"<<endl;    for(int k=0; k<OBJECT_NUM; k++)    {        cout<<mayResult[k]<<" ";    }    cout<<endl;    return 0;}

贪心策略与其他算法的区别


1.贪心与递推:与递推不同的是,贪心法中推进的每一步不是依据某一固定的递推式,而是当前看似最佳的贪心决策,不断的将问题归纳为更加小的相似的子问题。所以归纳、分析、选择正确合适的贪心策略,是正确解决贪心问题的关键。
2.贪心与动态规划:与动态规划不同的是,贪心是鼠目寸光;动态规划是统揽全局。


例题一:删数问题


键盘输入一个高精度的正整数n(n<=240位),去掉其中任意s个数字后剩下的数字按照原来的次序将组成一个新的正整数。编程对给定的n和s,寻求一种方案,使得剩下组成的新数最小。

样例输入

178543
4

样例输出

13

**

分析

**
*由于正整数n的有效位数最大可达240位,所以可以采用字符串类型来存储n。那么,应如何来确定该删除哪s位呢?是不是只要删掉最大的s个数字就可以了呢?
*为了尽可能地逼近目标,我们选取的贪心策略为:每一步总是选择一个使剩下的数最小的数字删去,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字,否则删除第一个递减区间的首字符。然后回到串首,按上述规则再删除下一个数字。重复以上过程s次,剩下的数字串便是问题的解了。

代码

    #include <cstdi0>      #include <cmath>       int main()      {          int a[100];          int x;          int k;          scanf("%d%d",&x,&k);          int len=log10(x)+1;          for(int i=len-1;i>=0;i--)          {              a[i]=x%10;              x/=10;          }          for(int i=0;i<k;i++)          {              int j;              for(j=0;j<len-i-1;j++)              {                  if(a[j]>a[j+1])                      break;              }              for(int z=j;z<len-i;z++)              {                  a[z]=a[z+1];              }          }          for(int i=0;i<len-k;i++)          {                  printf("%d",a[i]);          }          printf("\n");          return 0;       }   

**

例题2:排队问题

**
在一个食堂,有n个人排队买饭,每个人买饭需要的时间为ti,请你找出一种排列次序,使每个人买饭的时间总和最小。

样例输入

6
3 7 1 9 5 11

样例输出

91

**

【思路点拨】

**
由题意可知,本题可以采用的贪心策略为:将n个人排队买饭的时间从小到大排序,再依次累加每人的买饭时间,即可得到最小的总和。由样例可知,排好序后的数据为(1,3,5,7,9,11),每个人的买饭时间如下:
T1=t1=1
T2=T1+t2=1+3=4
T3=T2+t3=4+5=9
T4=T3+t4=9+7=16
T5=T4+t5=16+9=25
T6=T5+t6=25+11=36
总时间T=T1+T2+T3+T4+T5+T6=91
用反证法证明如下:假设一个不排好序的n个人也能得到最优答案,比如把(1,3,5,7,9,11)中的3,5对调一下,得到的序列为(1,5,3,7,9,11)。对调后,3,5前后的1,7,9,11四个人的买饭时间不会有变化,关键的变化是3,5两个人。这时,5这个人的买饭时间为1+5,3这个人的买饭时间变为1+5+3,此时两个人的总买饭时间中,5被累加了2次,而原方案中是3被累加了2次,其他一样。由此,两者相比较,可知有序的序列能得到最优的方案。对于其他位置的改变可以采用同样的方法证明。用反证法证明时,关键是证明反例不成立,由此推出原方案是最优的。

这道题就不给代码了,写出来的同志呢可以在言论那回复我,别忘点个赞哦!

原创粉丝点击