贪心专题


2019.2.13 晴空万里 心情愉悦


该专题主要涉及两种题型:

  • 简单贪心(问题B~G)
  • 区间贪心(问题A)


问题 A: 看电视

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

分析

该题为区间贪心,即给定n个区间,求不相交区间的最多个数

  1. 将所有区间按照x从大到小排列,若x相同则按y从小到大排列
  2. 遍历n个区间,依次选取不相交区间

完整Code(C/C++)

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 110;
struct Interval {//开区间 
	int x,y;
}I[maxn];
//sort函数使用的比较函数 
bool cmp(Interval a, Interval b){
	if(a.x != b.x)return a.x > b.x;//先按左端点从大到小排序 
	else return a.y < b.y;//左端点相同,按右端点从小到大排序 
}
int main(){
	int n;
	while(scanf("%d",&n),n != 0){//当输入n为0,跳出循环 
		for(int i = 0; i < n; i++){
			scanf("%d%d",&I[i].x,&I[i].y);
		}
		sort(I,I+n,cmp);//排序 
		int ans = 1,lastX = I[0].x;//分别表示无交集区间个数,上个选择区间的左端点初始值为排序后最右边的区间 
		for(int i = 1; i < n; i++){
			if(I[i].y <= lastX){// 如果当前区间与上个选择的区间无交集 
				ans++;
				lastX = I[i].x;//更新 
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

问题 B: 出租车费

题目描述
某市出租车计价规则如下:起步4公里10元,即使你的行程没超过4公里;接下来的4公里,每公里2元;之后每公里2.4元。行程的最后一段即使不到1公里,也当作1公里计费。
一个乘客可以根据行程公里数合理安排坐车方式来使自己的打车费最小。
例如,整个行程为16公里,乘客应该将行程分成长度相同的两部分,每部分花费18元,总共花费36元。如果坐出租车一次走完全程要花费37.2元。
现在给你整个行程的公里数,请你计算坐出租车的最小花费。
输入
输入包含多组测试数据。每组输入一个正整数n(n<10000000),表示整个行程的公里数。
当n=0时,输入结束。
输出
对于每组输入,输出最小花费。如果需要的话,保留一位小数。
样例输入
3
9
16
0
样例输出
10
20.4
36

分析

根据题意计算出分段公式,同时计算出每一段的单价,由于4~8公里的单价最低,当路程>=14时尽可能多选它

完整Code

#include<cstdio>
using namespace std;
//分段计费公式,同时得出每段单价,尽可能多得选取4~8有利于减少费用 
double Cal(int x){
	if(x >= 0 && x <= 4)return 10;
	else if(x > 4 && x <= 8)return 2*x+2;
	else return 2.4*x-1.2;
}

int main(){
	int n;
	while(scanf("%d",&n),n != 0){
		double ans = 0;
		while(n >= 14){//当公里数x>=14时,分解为8和x-8最为合算 
			ans += 18; 
			n -= 8;
		}
		ans += Cal(n);//最后剩余计算 
		if(ans - (int)ans > 0)printf("%.1lf\n",ans);//判断是否为小数,控制为一位小数 
		else printf("%.0lf\n",ans);
	}	
	return 0;
} 

问题 C: To Fill or Not to Fill(暂时不会)

问题 D: Repair the Wall

题目描述
Long time ago , Kitty lived in a small village. The air was fresh and the scenery was very beautiful. The only thing that troubled her is the typhoon.
When the typhoon came, everything is terrible. It kept blowing and raining for a long time. And what made the situation worse was that all of Kitty’s walls were made of wood.
One day, Kitty found that there was a crack in the wall. The shape of the crack is
a rectangle with the size of 1×L (in inch). Luckly Kitty got N blocks and a saw(锯子) from her neighbors.
The shape of the blocks were rectangle too, and the width of all blocks were 1 inch. So, with the help of saw, Kitty could cut down some of the blocks(of course she could use it directly without cutting) and put them in the crack, and the wall may be repaired perfectly, without any gap.
Now, Kitty knew the size of each blocks, and wanted to use as fewer as possible of the blocks to repair the wall, could you help her ?
输入
The problem contains many test cases, please process to the end of file( EOF ).
Each test case contains two lines.
In the first line, there are two integers L(0<L<1000000000) and N(0<=N<600) which
mentioned above.
In the second line, there are N positive integers. The ith integer Ai(0<Ai<1000000000 ) means that the ith block has the size of 1×Ai (in inch).
输出
For each test case , print an integer which represents the minimal number of blocks are needed.
If Kitty could not repair the wall, just print “impossible” instead.
样例输入
2 2
12 11
14 3
27 11 4
109 5
38 15 6 21 32
5 3
1 1 1
样例输出
1
1
5
impossible

分析

1.不存在:所有块的长度总和<L
2.N个: 所有块的长度总和=L
3.求最小个数:所有块的长度总和>L
	3.1 所有block按照长度由大到小排序
	3.2 遍历所有block,若当前块长度lcur >= L,个数ans++,进行下一次循环;否则,个数ans++,L=L-l[cur] 

完整Code

#include<cstdio>
#include<algorithm>
using namespace std;
//按照长度降序排列 
bool cmp(long a, long b){
	if(a != b)return a > b;
}

int main(){
	long L,N;
	while(scanf("%ld%ld",&L,&N) != EOF){
		long l[N+3],sum = 0;
		//输入 
		for(int i = 0; i < N; i++){
			scanf("%ld",&l[i]);
			sum += l[i];
		}
		//三个判断 
		if(sum < L){
			printf("impossible\n");
			continue;
		}
		else if(sum == L){//等于直接得答案 
			printf("%ld\n",N);
			continue;
		}
		else {//求最优解 
			sort(l,l+N,cmp);
			long ans = 0;
			for(int i = 0; i < N; i++){
				if(l[i] >= L){
					ans++;
					break;
				}
				else{
					ans++;
					L -= l[i];
				}
			}
			printf("%ld\n",ans);
		}
	}
	return 0;
} 

问题 E: FatMouse’s Trade

题目描述
FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food, JavaBean.
The warehouse has N rooms. The i-th room contains J[i] pounds of JavaBeans and requires F[i] pounds of cat food. FatMouse does not have to trade for all the JavaBeans in the room, instead, he may get J[i]* a% pounds of JavaBeans if he pays F[i]* a% pounds of cat food. Here a is a real number. Now he is assigning this homework to you: tell him the maximum amount of JavaBeans he can obtain.
输入
The input consists of multiple test cases. Each test case begins with a line containing two non-negative integers M and N. Then N lines follow, each contains two non-negative integers J[i] and F[i] respectively. The last test case is followed by two -1’s. All integers are not greater than 1000.
输出
For each test case, print in a single line a real number accurate up to 3 decimal places, which is the maximum amount of JavaBeans that FatMouse can obtain.
样例输入
4 2
4 7
1 3
5 5
4 8
3 8
1 2
2 5
2 4
-1 -1
样例输出
2.286
2.500

完整Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 10001;
struct Food{
	double j,f;//最好都用double,否则计算过程中可能会由于类型转换出错 
	double val;//单价,val=j/f 
}Food[maxn];
//将结构体按照val降序排列 
bool cmp(struct Food a, struct Food b){
	if(a.val != b.val)return a.val > b.val;
}
int main(){
	int M,N;
	while(scanf("%d%d",&M,&N),M != -1){
		for(int i = 0; i < N; i++){
			scanf("%lf%lf",&Food[i].j,&Food[i].f);
			Food[i].val = Food[i].j/Food[i].f;
		}
		sort(Food,Food+N,cmp);//降序排列 
		double sum=0;//总和 
		for(int i = 0; i < N; i++){
			if(M <= Food[i].f){//已有食物不超过当前f 
				sum += M*Food[i].val;
				break; 
			}
			else {
				sum += Food[i].j;
				M -= Food[i].f;
			}
		}
		printf("%.3lf\n",sum);
	}
	return 0;
} 

问题 F: 迷瘴

题目描述

小明正在玩游戏,他控制的角色正面临着幽谷的考验——
幽谷周围瘴气弥漫,静的可怕,隐约可见地上堆满了骷髅。由于此处长年不见天日,导致空气中布满了毒素,一旦吸入体内,便会全身溃烂而死。
幸好小明早有防备,提前备好了解药材料(各种浓度的万能药水)。现在只需按照配置成不同比例的浓度。
现已知小明随身携带有n种浓度的万能药水,体积V都相同,浓度则分别为Pi%。并且知道,针对当时幽谷的瘴气情况,只需选择部分或者全部的万能药水,然后配置出浓度不大于 W%的药水即可解毒。
现在的问题是:如何配置此药,能得到最大体积的当前可用的解药呢?
特别说明:由于幽谷内设备的限制,只允许把一种已有的药全部混入另一种之中(即:不能出现对一种药只取它的一部分这样的操作)。

输入

输入数据的第一行是一个整数C,表示测试数据的组数;
每组测试数据包含2行,首先一行给出三个正整数n,V,W(1<=n,V,W<=100);
接着一行是n个整数,表示n种药水的浓度Pi%(1<=Pi<=100)。

输出

对于每组测试数据,请输出一个整数和一个浮点数;
其中整数表示解药的最大体积,浮点数表示解药的浓度(四舍五入保留2位小数);
如果不能配出满足要求的的解药,则请输出0 0.00。

样例输入
2
1 35 68
1
2 79 25
59 63
样例输出
35 0.01
0 0.00

完整Code

#include<cstdio>
#include<algorithm>
using namespace std;

bool cmp(double a, double b){
	return a < b;
}
int main(){
	int C,n;
	double V,W;
	scanf("%d",&C);
	for(int i = 0; i < C; i++){
		scanf("%d%lf%lf",&n,&V,&W);
		double rate[n];
		for(int j = 0; j < n; j++){
			scanf("%lf",&rate[j]);
		}
		sort(rate,rate+n,cmp);//按照浓度升序排列 
		if(rate[0] > W)printf("0 0.00\n");//最小浓度都高于W,无论怎么混合都比W大 
		else{
			double ans=1,p=rate[0];//初始化:使用最低浓度 
			for(int k = 1; k < n; k++){
				if((rate[k]+p*ans)/(ans+1) <= W){//判断当前浓度能否混合 
					p = (rate[k]+p*ans)/(ans+1);
					ans++;
				}
				else break;
			}
			printf("%.0lf %.2lf\n",ans*V,p/100);//p是带有%,输出时除以100 
		}
	}
	return 0;
} 

问题 G: 找零钱

题目描述

小智去超市买东西,买了不超过一百块的东西。收银员想尽量用少的纸币来找钱。
纸币面额分为50 20 10 5 1 五种。请在知道要找多少钱n给小明的情况下,输出纸币数量最少的方案。 1<=n<=99;

输入

有多组数据 1<=n<=99;

输出

对于每种数量不为0的纸币,输出他们的面值*数量,再加起来输出

样例输入

25
32

样例输出

20*1+5*1
20*1+10*1+1*2

完整Code

#include<cstdio>
#include<algorithm>
using namespace std;

int main(){
	int n,price[5]={50,20,10,5,1};
	while(scanf("%d",&n) != EOF){
		for(int i = 0; i < 5; i++){
			int m = price[i];
			if(n%m > 0){
				if(n/m != 0)printf("%d*%d+",m,n/m);
				n = n%m;
			}
			else if(n != 0){
				if(n/m != 0)printf("%d*%d\n",m,n/m);
				break;
			}
		}
	} 
	return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值