贪心问题初步

首选理解一下贪心问题:贪心问题是一种从局部最优得到全局最优的问题,意思是立足于一点,从这点出发开始想最优解,一步步得出最终解。
通过上面口语化的描述,可见一个贪心问题对关键就是如何立足于一点寻找最优解,这里的关键就是最优解的度量方法。
下面是一个贪心问题的通用流程:

proceduce Greedy(A,n){//A中包含n个输入
解向量solution初始为空
从1到n
按照最优度量选出A中一个输入x  //**最优度量**
如果可以加入solution则加入   //**如何判断**
重复上述过程
返回解向量结果

由上可见,如果把握好最优度量标准和判断条件,那么问题也就基本解决了,下面来看实战:

1.基础简单贪心问题

背包问题: 已知n种物品和一个可以容纳重量为M的背包,物品重量收益用wi和pi表示,试求怎样将物品放入背包总收益最大。

分析:很显然这个问题很容易让我们想到买商品时的单价,如果用wi/pi表示单位收益再非增排序,依次选取放入背包即可。 其中wi/pi就是度量标准,判断条件只要重量和小于M就可以继续加入,直到相等。

2.区间贪心问题

看电视 :
暑假到了,小明终于可以开心的看电视了。但是小明喜欢的节目太多了,他希望尽量多的看到完整的节目。
现在他把他喜欢的电视节目的转播时间表给你,你能帮他合理安排吗?
输入
输入包含多组测试数据。每组输入的第一行是一个整数n(n<=100),表示小明喜欢的节目的总数。
接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。
当n=0时,输入结束。
输出
对于每组输入,输出能完整看到的电视节目的个数。

分析:上面的问题明显相当于给定一堆(a,b)的区间,叫我们挑选出不相交的最多区间数,这就是典型的区间贪心问题。首先如果两个区间大包小,我们肯定选择小的那个;如果两个区间相交,是不是可以把其中一个区间视为两部分,一部分是单独的,另一个部分是相交部分,单独的部分不影响,相交部分又转化为大包小问题,大包小取小,实质就取了被划分的那个区间。
根据上述思路,我们可以从左端点开始,也可以从右端点开始选取,然后贯彻上面那个思路收集符合条件的区间。

#include<algorithm> 
using namespace std;
const int maxn=100;
int n=1,sum;
struct tv{
	int a,b;
	bool operator<(const tv &c)const{
	if(c.b==b)return a>c.a;    
	return b<c.b;
	}
}T[100];

int main(){
	
	while(scanf("%d",&n),n!=0){
	sum=1;
	for(int i=0;i<n;i++){
		scanf("%d %d",&T[i].a,&T[i].b);
	}
	sort(T,T+n); //从右端点开始,从小到大排序
	int tag=0;
	for(int j=1;j<n;j++){
		if(T[tag].b<=T[j].a){  //找不相交点(主要有相交就取前一个)
			sum++;
			tag=j;
		}
	}
	printf("%d\n",sum);
}
    
	return 0;
} 

此外这个问题还有一些变种,值得注意,如区间选点问题,区间覆盖问题。

3.综合应用

汽车加油问题: 你要从杭州开车到目的地,整条路线是一个数轴,你在数轴上的0点,目的地在距离0点为D米,你的车油罐的容量为C,每单位油可以行驶Davg 米,在路线上有n个加油站,每个加油站在位置为di处,单价是pi, 问你,从0点出发开到目的地所需要的最小花费是多少,若开不到目的地,输出最远可以到达的地方,油箱一开始为空。

分析:该问题的起点就在原点,如果此处没有加油站,肯定无法到达,如果有,立足于此加油站则思考下一个问题
如果能找满油最大行程到距离该加油站最近的价格更低的加油站,只加够到那里的油
如果没有则找最大行程内价格最低的油加满油到那
如果一个最大行程里没有加油站,凉凉,无法到达

#include<iostream>
#include<algorithm> 
using namespace std;
const int maxn=501;
struct station{
	double d,p;
	bool operator<(const station &a)const{
	if(a.d==d)return p<a.p;
	return d<a.d;
	}	
}T[maxn];

int n; 
double D,Davg,C,M,sum=0;

int main(){
	scanf("%lf%lf%lf%d",&C,&D,&Davg,&n);
	for(int i=0;i<n;i++){
	scanf("%lf%lf",&T[i].p,&T[i].d);}
	sort(T,T+n);   //           输入加油站并按距离出发点从小到大排序 
	M=C*Davg;
	if(T[0].d!=0)  //          如果出发点没有加油站 
	printf("The maximum travel distance = %.2f",0.00);
	else{
	   double nowtank,need,minprice;
	   int now=0,then,flag=0;
	   T[n].d=D;T[n].p=0;
	   
	   while(now<n){
	   	then=-1;
	   	minprice=T[now+1].p;
	   for(int i=now+1;(T[i].d-T[now].p)<=M&&i<=n;i++)
	   { 
	     
	     if(T[i].p<=minprice){
	     then=i;
		 minprice=T[i].p;} 
	   	if(T[i].p<T[now].p)
	   		break;
			   	
	   }
	   if(then==-1) break;                          //第三种情况处理 
	   need=(T[then].d-T[now].d)/Davg;
	   
	   if(minprice<T[now].p){ 
	                                              //第一种情况处理 
	   	if(need<=nowtank)
	   	nowtank-=need;
	   	else{                                              
	   		sum+=(need-nowtank)*T[now].p;
	   		nowtank=0;
		   }	
	   }
	     else{                                 //第二种情况处理       
	   	sum+=(C-nowtank)*T[now].p;
	   	nowtank=C-need;
	   }
	   now=then;flag=0; 
	   }
	   
	  if(now==n)
	printf("%.2f\n",sum);
	 else
		printf("The maximum travel distance = %.2f",T[now].d+M);
		
	} 	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值