2019年3月13日

贪心算法
所谓贪心算法,其实是一种思想,即通过把对问题的每个部分分别求部分最优解,从而解出整体最优解。
所以贪心算法特别适合最优解问题。
下面举两个例题。
(1)背包问题
给定一个载重量为M的背包,考虑n个物品,其中第i个物品的重量 ,价值wi (1≤i≤n),要求把物品装满背包,且使背包内的物品价值最大(物品可以分割)。
对于这个题目,就要引入性价比这一感念,即价值与质量的比值,性价比高的优先放入。
对于其实现

 struct bag
 {           //定义变量
  	int w;   //物品的重量
  	int v;   //物品的价值 
  	double c;  //性价比 
  	double x;  //装入背包的量,0≤x≤1
  	int index;  //物品编号
  	
  }a[1001];
  
   //存放物品的数组按照性价比排序
   bool cmp(bag a, bag b)
   { 
   	return a.c >= b.c;
   } 
   
   double knapsack(int n, bag a[], double c)
   {
   	double cleft = c;//背包可携带质量
    	int i = 0;//计数  
    	double b = 0;//价值计数  
    	while(i<n && a[i].w<=cleft//物品质量小于背包容许容量)
    	{
    		cleft -= a[i].w;//求剩下还有多少容量
    		b += a[i].v;//价值计数    
    		//物品原先的序号是a[i].index,全部装入背包    
    		a[a[i].index].x = 1.0;
    		i++;  
    }  
   在while完成后可能有两种情况  
  (1)i>n 即物品全部放入背包
  (2) a[i].w>cleft 背包剩余容量不足以在放进一整个物品  
   	if (i<n)   
  	 {    
   		a[a[i].index].x = 1.0*cleft/a[i].w;//将物品分割    
   		b += a[a[i].index].x*a[i].v;//分割后价值  
   	}  
   		return b;
   	} 

(2)最优装载问题
有一批集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
对这个问题来说,求的是可能多的集装箱装上轮船,是一个最优解问题。
于是,我们可以用贪心来做。因为不管体积,而且要尽量多的装,所以要选最轻的往上装。

struct load 
{
 	int index;  //集装箱编号
 	int w;   //集装箱重量
}box[1001];

排序(按集装箱的重量升序):
bool cmp (load a, load b) 
{
 	if (a.w<b.w) return true;
 	else return false;
}

while (scanf("%d%d", &c, &n)!=EOF)
{
  memset(box, 0, sizeof(box));
  memset(x, 0, sizeof(x));
  for (int i=1; i<=n; i++) 
  {
    scanf("%d", &box[i].w);
    box[i].index = i;
  }
  //按集装箱的重量升序排序
  stable_sort(box, box+n+1, cmp);
  if (box[1].w>c) {
    printf("No answer!\n");
    continue;
  }
  
//贪心算法的实现,重量最轻者先装载
  int i;
  for (i=1; i<=n && box[i].w<=c; i++)
  {
    x[box[i].index] = 1;
    c -= box[i].w;
  }
  //输出装载的集装箱数量
  printf("%d\n", i-1);
  //输出装载的集装箱编号
  for (i=1; i<=n; i++)
    if (x[i]) printf("%d ", i);
  printf("\n");
}

(3)删数问题
给定n位正整数a,去掉其中任意k≤n个数字后,剩下的数字按原次序排列组成一个新的正整数。对于给定的n位正整数a和正整数k,设计一个算法找出剩下数字组成的新数最小的删数方案。
从描述中可以看到,求的是剩下数字组成的新数最小的删数方案,是最优解问题。
对于这个题目,PPT上的代码是这样的

string a;        //n位数a
int k;
cin>>a>>k;
//如果k≥n,数字被删完了
If (k >= a.size())  a.erase();
else while(k > 0)
{
  //寻找最近下降点
  int i;
  for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]);  ++i);
  a.erase(i, 1);    //删除xi
  k- -;
}
//删除前导数字0
while(a.size() > 1 && a[0] == '0') 
  a.erase(0, 1);
cout<<a<<endl;

对于这个代码,这里打错了

//寻找最近下降点
  int i;
  for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]);  ++i);
  a.erase(i, 1);    //删除xi
  k- -;
}

这个会把小的那一个删除。
所以应该是这样的

//寻找最近下降点
  int i;
  for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]);  ++i);
  a.erase(i+1, 1);    //删除xi
  k- -;
}

以上。

猜你喜欢

转载自blog.csdn.net/qq_17679843/article/details/88544829