贪心练习题集

洛谷-纪念品分组(1094)

题目链接

问题分析:

根据问题描述,得出最优解应该满足分组尽可能少。此外要求,每组至多两件物品且价值尽量均匀,而且还不能超过规定价格。
找出问题所在,解便不难得出。倘若价值最大的和价值最小的都没有超过规定价格,则次小和次大两两组合也能满足要求。如若不能,则价值最大的便自己单独一组,最小的和次大的组合再次比较,看是否满足要求。

代码示例:
#include<iostream>
#include<algorithm>
using namespace std;

int main(){
 	int w, n;
 	cin >> w >> n;
 	int value[n];
 	for(int i = 0; i < n; i++){
  	cin >> value[i];
 	}
 	sort(value, value+n);
 	int i =0, j = n-1, count = 0;
 	while(i <= j){
  		if(value[i] + value[j] <= w){//寻找价值较大的同价值较小的组合
   			j--;
   			i++;
  		} else {//价值较大的单独放置
   			j--;
  		}
  		count++;
 	}
 	cout << count;
 	return 0;
}

上述代码,对程序执行贡献最大的是一次排序,需要O(nlogn)的时间,因此时间复杂度为O(nlogn)。

洛谷-合并果子(1090)

求解过程

最大整数

题目链接

问题分析:

典型的贪心求解问题,问题的最优解要求得到的整数最大。因此每次局部求解过程中,都应该找到序列中的最大值。这里的局部最大值,并非真正意义上的最大值,而是应该选择每个数的第1、2、3……位上的数值尽可能大的数。例如123和72,在实际意义上,123>72,但是本题,72的首位是7,123首位是1,显然7>1,因此应首选72,而不是123。

代码示例:
#include<iostream>
#include<algorithm>
using namespace std;

bool cmp(string a, string b){
 	if(a + b > b + a) return 1;
 	else return 0;
}

int main(){
 	int n;
 	cin >> n;
 	string ars[n];
 	string res = "";
 	for(int i = 0; i < n; i++)
  	cin >> ars[i];
 	sort(ars, ars + n, cmp);
 	for(int i = 0; i < n; i++)
  		res += ars[i];
 		cout << res;
 	return 0;
}
美元汇率

题目链接

问题分析:

初看题目,想的太复杂,原因在于以下此种情况的考虑:

样例:5
400 600 300 500 300 250

如上例,第一天购买,但相邻的第二天却不符合,是否需要继续向后匹配?开始的想法是需要。但是分析发现,题目要求最大收益,什么情况收益最大?==稳赚不赔的情况下进行尽量多次数的交换。==仍如上例情况,第二天买,第三天卖的收益肯定是大于第一天的,原因在于第二天能够买到的马克大于第一天(第二天与第三天的差值要更大-利润空间更大)。

代码示例:
#include<cstdio>
using namespace std;

int main(){
 	int n;
 	scanf("%d", &n);//天数
 	int m[n];
 	double t = 100.0;
 	for(int i = 0; i < n; i++){
  		scanf("%d", &m[i]);
 	} 
 	for(int i = 0; i < n-1; i++){
  		if(m[i] > m[i+1])
   			t *= (double)m[i]/m[i+1];
 	}
 	printf("%.2f", t);
 	return 0;
}
/*
5
400 300 500 300 250*/
零件分组:

题目链接

问题分析:

题目要求对零件长度和重量分组,且长度和重量皆呈不下降趋势。
法一:可以先确保长度或重量二者之一呈不下降趋势(排序实现)。另一属性可以采用暴力遍历求解:遍历序列,直至没有逆序对为止。举个例子,如下图所示:
在这里插入图片描述
如图所示,已按其中之一属性排序,因此只需要求解对立属性序列即可。遍历第二行序列,若存在逆序对(8,4即为逆序对)则分组记录加1,暴力扫描直至不存在逆序对。我们可以设置一个布尔类型数组,若元素满足条件(呈不下降趋势)则可以设置数组为false,表示此数组已加入一个分组。例如,3 < 5 < 8满足条件,因此三个加入一组(布尔类型数组置false,表示已从待排序列中删除),此组中的最大值已为8,后续序列无满足情况,所以需要重新安排一组。

代码示例:
#include<iostream>
#include<algorithm>
using namespace std;

struct A{
 	int h, w;
};

bool cmp(A x, A y){
 	if(x.h == y.h) return x.w < y.w;
 	return x.h < y.h;
}

int main(){
 	int n, count = 0, b = 0, b2 = -1;
 	cin >> n;
 	bool visted[n];
 	bool flag = true;
 	A a[n];
 	for(int i = 0; i < n; i++){//init 
  		visted[i] = true;
  		cin >> a[i].h >> a[i].w;
 	}
 	sort(a, a+n, cmp);
 	while(b != b2){
  		b2 = b;
  		count++;
  		int t = a[b].w;
  		for(int i = b+1; i < n; i++){
   			if(a[i].w >= t && visted[i]){
   		 		t = a[i].w;
    				visted[i] = false;
   			} else if(a[i].w < t && flag){
    				b = i;
    				flag = false;
   			}
  	}
  		flag = true;
 	}
 	cout << count;
 	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值