《算法的乐趣》——贪心算法(贪婪法greedy algorithm)

1.定义

下面标黄的都是贪心很重要的点

2.贪心的基本思想

贪心的具体实施方式有以下两种:

扫描二维码关注公众号,回复: 2934463 查看本文章

3.例子:贪心解决0-1背包问题

该问题隐含了一个条件,每个物品只有一件,也就是限定了每件物品只能选择0个或者1个,因此称之为0-1背包问题

贪心的策略总共有三种:

第一种:

根据物品价值选择,每次都选价值最高的物品。

根据这个策略,最终选择装入背包的物品编号是4、2、6、5,此时包中物品总重量是130,总价值是165

第二种:

根据物品重量选择,每次都选择重量最轻的物品。

根据这个策略,最终选择装入背包的物品编号依次是6、7、2、1、5,此时包中物品总重量是140,总价值是155

第三种:

定义一个价值密度的概念,每次选择都选价值密度最高的物品。

C++封装的,大牛还是叼

// knapsack.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>

//物品的两个属性:重量和价值
typedef struct tagObject
{
    int weight;
    int price;
    int status; //0:未选中;1:已选中;2:已经不可选
}OBJECT;

//背包问题的两个属性:一个是可选物品列表,一个是背包总的称重量
typedef struct tagKnapsackProblem
{
    std::vector<OBJECT> objs;
    int totalC;
}KNAPSACK_PROBLEM;


typedef int (*SELECT_POLICY)(std::vector<OBJECT>& objs, int c);

int Choosefunc1(std::vector<OBJECT>& objs, int c)
{
    int index = -1;
    int mp = 0;
    for(int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if((objs[i].status == 0) && (objs[i].price > mp))
        {
            mp = objs[i].price;
            index = i;
        }
    }

    return index;
}

int Choosefunc2(std::vector<OBJECT>& objs, int c)
{
    int index = -1;
    int mw = 10000;
    for(int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if((objs[i].status == 0) && (objs[i].weight < mw))
        {
            mw = objs[i].weight;
            index = i;
        }
    }

    return index;
}

int Choosefunc3(std::vector<OBJECT>& objs, int c)
{
    int index = -1;
    double ms = 0.0;
    for(int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if(objs[i].status == 0)
        {
            double si = objs[i].price;
            si = si / objs[i].weight;
            if(si > ms)
            {
                ms = si;
                index = i;
            }
        }
    }

    return index;
}

void PrintResult(std::vector<OBJECT>& objs)
{
    int totalW = 0;
    int totalP = 0;
    for(int i = 0; i < static_cast<int>(objs.size()); i++)
    {
        if(objs[i].status == 1)
        {
            totalW += objs[i].weight;
            totalP += objs[i].price;
            std::cout << "object " << i + 1 << ": weight=" << objs[i].weight << 
                                               ", price=" << objs[i].price << std::endl;
        }
    }
    std::cout << "total weight : " << totalW << ", total price : " << totalP << std::endl; 
}

//贪心的步骤:子问题的分解和选择策略
//spFunc参数是选择策略函数的接口,通过替换这个参数可以得到三种贪心策略
void GreedyAlgo(KNAPSACK_PROBLEM *problem, SELECT_POLICY spFunc)
{
    int idx;
    int ntc = 0;

    //spFunc 每次选最符合策略的那个物品,选后再检查
    while((idx = spFunc(problem->objs, problem->totalC - ntc)) != -1)
    {
        //所选物品是否满足背包承重要求?
        if((ntc + problem->objs[idx].weight) <= problem->totalC)
        {
            problem->objs[idx].status = 1;
            ntc += problem->objs[idx].weight;
        }
        else
        {
            //不能选这个物品了,做个标记后重新选
            problem->objs[idx].status = 2; 
        }
    }

    PrintResult(problem->objs);
}

const int MIN=0x80000000;
const int N=7;   //物品数量
const int V=150;  //背包容量
int f[N+1][V+1];

int Package(int *W,int *C,int N,int V)
{
 int i,j;
 memset(f,0,sizeof(f));  //初始化为0

 for(i=0;i<=N;i++)
 for(j=1;j<=V;j++)               //此步骤是解决是否恰好满足背包容量,
  f[i][j]=MIN;                //若“恰好”满足背包容量,即正好装满背包,则加上此步骤,若不需要“恰好”,则初始化为0
    
 for(i=1;i<=N;i++)
  for(j=C[i];j<=V;j++)
  {
   f[i][j]=(f[i-1][j]>f[i-1][j-C[i]]+W[i])?f[i-1][j]:(f[i-1][j-C[i]]+W[i]);
   std::cout<<"f["<<i<<"]["<<j<<"]="<<f[i][j]<<std::endl;
  }
 return f[N][V];
}

void DPAlgo()
{
 int W[8]={0,10,40,30,50,35,40,30};      //物品权重
 int C[8]={0,35,30,60,50,40,10,25};      //物品大小
 int result=Package(W,C,N,V);
 if(result>0)
 {
  std::cout<<std::endl;
  std::cout<<"the opt value:"<<result<<std::endl;
  int i=N,j=V;
  while(i)
  {
   if(f[i][j]==(f[i-1][j-C[i]]+W[i]))
   {
    std::cout<<i<<":"<<"w="<<W[i]<<",c="<<C[i]<<std::endl;
    j-=C[i];
   }
   i--;
  }
 }
 else
     std::cout<<"can not find the opt value"<<std::endl;
}

OBJECT objects[] = { {35,10,0}, {30,40,0}, {60,30,0}, {50,50,0},
                     {40,35,0}, {10,40,0}, {25,30,0} };

int main(int argc, char* argv[])
{

    KNAPSACK_PROBLEM problem;

    problem.objs.assign(objects, objects + 7);
    problem.totalC = 150;

    //GreedyAlgo(&problem, Choosefunc1);
    //GreedyAlgo(&problem, Choosefunc2);
    //GreedyAlgo(&problem, Choosefunc3);
    DPAlgo();

	return 0;
}

需要说明的有:

1)从主函数看起,首先是初始化数据的输入,然后是 GreedyAlgo(&problem, Choosefunc1);这里用了个typedef int (*SELECT_POLICY)(std::vector<OBJECT>& objs, int c);(*SELECT_POLICY)的指针函数来表示选择不同的Choosefunc1、Choosefunc2、Choosefunc3.

2)不同的Choosefunc的输入参数包括:结构体数组以及int c,他们的返回值都是index序号,用在void GreedyAlgo()的idx中,由于是0-1背包问题,在下面的结构体成员变量的int status中来表示三种选择状态,这也在void GreedyAlgo()是有所体现的。

typedef struct tagObject
{
    int weight;
    int price;
    int status; //0:未选中;1:已选中;2:已经不可选
}OBJECT;

OBJECT结构体枚举,用在了main的输入

OBJECT objects[] = { {35,10,0}, {30,40,0}, {60,30,0}, {50,50,0},
                     {40,35,0}, {10,40,0}, {25,30,0} };

结果展示:

GreedyAlgo(&problem, Choosefunc1);

GreedyAlgo(&problem, Choosefunc2)

 GreedyAlgo(&problem, Choosefunc3)

DP的话先留一下

猜你喜欢

转载自blog.csdn.net/u011436427/article/details/82120349