ACWing--基础算法--贪心(部分题解)

目录

906.区间问题--区间分组

148.Huffman树(哈夫曼树)--合并果子

913.排序不等式--排队打水 

104.货仓选址

125.耍杂技的牛


906.区间问题--区间分组

原题:

给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数。

输入格式

第一行包含整数 N,表示区间数。

接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。

输出格式

输出一个整数,表示最小组数。

数据范围

1≤N≤105,
−109≤ai≤bi≤109

输入样例:

3
-1 1
2 4
3 5

输出样例:

2
难度:简单
时/空限制:1s / 64MB
总通过数:27839
总尝试数:57884
来源:

模板题

算法标签

先上代码: 

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10;

struct Range{
	int l,r;
	bool operator < (const Range &w)const//运算符重载
	{
		return l<w.l;//按照区间左端点从小到大排序
	}
}range[N];

int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++) cin>>range[i].l>>range[i].r;
	
	sort(range,range+n);
	
	//是定义一个小根堆的优先队列,并按照从小到大的顺序排列,即最小的元素位于小根堆的顶端,较大的数放在较下的位置	
	priority_queue<int,vector<int>,greater<int> > heap;
	
	for(int i=0;i<n;i++)
	{
	    //如果已存在的组的右端点>=r.l则说明有交集,则要开新组
		if(heap.empty()||heap.top()>=range[i].l) heap.push(range[i].r);
		
		//否则就放到最小值那个组里面去,更新栈顶。这样可以使得所分的组最少,这里体现了贪心的思想。
		else
		{
			heap.pop();
			heap.push(range[i].r);
		}
	}
	cout<<heap.size();
	return 0;	
} 

思路: 

通过举例来解释,设现有已经排好序的6个区间,如下图所示 。①~⑭为代码中for循环的过程,画了☆的表示这一步是在排序(每次有变化小根堆就会自动排序,将最小的值放到顶部,较大的值往下放,注意:堆只能访问根节点,其余结点无法操作)。

①:将区间1压入;(ps:压入的都是区间的右端点)

②:区间1的右端点大于区间2的左端点,说明两区间有交集,需要重新开一个组,将区间2压入;

③:小根堆内部排序,将区间1的右端点放在顶端,区间2往下放;

④:区间3的左端点大于区间1的右端点,将区间1弹出;

⑤:再将区间3压入;

⑥:小根堆排序;

⑦:区间4的左端点大于区间2的右端点,将区间2弹出;

⑧:再将区间4压入;

⑨:小根堆排序;

⑩:区间3的右端点大于区间5的左端点,说明两区间有交集,需要重新开一个组,将区间5压入;

⑪:小根堆排序;

⑫:区间6的左端点大于区间3的右端点,将区间3弹出;

⑬:再将区间6压入;

⑭:小根堆排序。

最后分组的结果是:

区间 1,3,6 为一组,区间 2,4 为一组,区间 5 为一组,共3组。

148.Huffman树(哈夫曼树)--合并果子

想了解哈夫曼树的朋友可以看这篇:【数据结构与算法】-哈夫曼树(Huffman Tree)与哈夫曼编码_哈夫曼树和哈夫曼编码-CSDN博客

 原题:

在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。

达达决定把所有的果子合成一堆。

每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。

可以看出,所有的果子经过 n−1 次合并之后,就只剩下一堆了。

达达在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以达达在合并果子时要尽可能地节省体力。

假定每个果子重量都为 11,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使达达耗费的体力最少,并输出这个最小的体力耗费值。

例如有 33 种果子,数目依次为 1,2,9。

可以先将 1、2堆合并,新堆数目为 3,耗费体力为 3。

接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12。

所以达达总共耗费体力=3+12=15。

可以证明 15 为最小的体力耗费值。

输入格式

输入包括两行,第一行是一个整数 n,表示果子的种类数。

第二行包含 n 个整数,用空格分隔,第 i 个整数 ai 是第 i 种果子的数目。

输出格式

输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。

输入数据保证这个值小于 231231。

数据范围

1≤n≤10000,
1≤ai≤20000

输入样例:

3 
1 2 9 

输出样例:

15
难度:简单
时/空限制:1s / 64MB
总通过数:31016
总尝试数:44324
来源:

《算法竞赛进阶指南》NOIP2004提高组模板题

算法标签

代码: 

#include<iostream>
#include<queue>
using namespace std;
const int N=1010;

int main()
{
	int n;
	cin>>n;
	priority_queue<int,vector<int>,greater<int> > heap;
	for(int i=0;i<n;i++)
	{
		int x;
		cin>>x;
		heap.push(x);
	}
	
	int sum=0;
	for(int i=0;i<n-1;i++)
	{
		int a=heap.top();
		heap.pop();
		int b=heap.top();
		heap.pop();
		heap.push(a+b);
		sum+=(a+b);
	}
	cout<<sum;
	return 0;
}

 思路:

用到了贪心+哈夫曼数(优先队列实现,优先队列又由小根堆实现),不难的(哪个不懂就去学)

①输入:用优先队列heap存,可以实现自动排序,最小的放在 top;

②将最小的数 a 取出并删去更新树,再将最小的数 b 取出并删去更新树;

③将 a+b 存入树,同时sum+=(a+b);

④将②和③循环 n-1 次结束;

⑤输出sum。

913.排序不等式--排队打水 

原题: 

有 n 个人排队到 1 个水龙头处打水,第 i 个人装满水桶所需的时间是 ti,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小?

输入格式

第一行包含整数 n。

第二行包含 n 个整数,其中第 i 个整数表示第 i 个人装满水桶所花费的时间 ti。

输出格式

输出一个整数,表示最小的等待时间之和。

数据范围

1≤n≤105,
1≤ti≤104

输入样例:

7
3 6 1 4 2 5 7

输出样例:

56

对题目中例子的分析:

 最后的56是怎么得来的?

要想时间最少,那么就要求打水时间越少的人排在越前面打水。所以我们先排序,最终打水顺序为:

1,2,3,4,5,6,7  他们分别需要等待的时间为:

1:等0分钟

2:等1分钟

3:等1+2分钟

......

7:等1+2+3+4+5+6分钟

所以等待时间总和为:

sum=0+1+(1+2)+(1+2+3)+...+(1+2+3+4+5+6)

法一(贪心+前缀和):

 代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int s[N];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	sort(a+1,a+n+1); // 因为下标是从1开始的,所以排序sort()函数也要相应地改一下
	
	for(int i=1;i<n;i++) // 循环条件不能写成 i<=n,因为这样会把最后一个人接水的时间也算上去,而实际上他接水的时间并不属于总的等待时间 
	{
		s[i]=s[i-1]+a[i];
	}
	
	long long int sum=0;
	for(int i=1;i<=n;i++) sum+=s[i];
	
	cout<<sum;
	return 0;
}

思路:

①输入,用数组存,注意下标从1开始,因为后面要用到前缀和,下标从1开始会更方便。

②用sort()函数对数组从小到大排序。

③求前缀和,s[i]=s[i-1]+a[i] 。(i从1开始)

④求时间和 sum+=s[i] 。

⑤输出 sum

法二(排序不等式):

 代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100005;
int main()
{
	int a[N];
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	sort(a, a + n);
	long long int sum = 0;
	for (int i = 0; i < n; i++)// 也可以换成 i<n-1 因为最后一项是 sum+=a[n-1]*0,加相当于不加,所以最后一项可以省略
	{
		sum += a[i] * (n - 1 - i);
	}
	cout << sum;
}

 思路:

从上面对题目例子的分析可以发现,实际上的总等待时间 sum 加了6次第一个人的打水时间,加了5次第二个的打水时间......,加了1次第六个人的打水时间,加了0次最后一个人的打水时间,所以可以推出公式:sum=a[1]*(n-1)+a[2]*(n-2)+a[3]*(n-3}+...+a[n-1]*1+a[n]*0

104.货仓选址

原题:

在一条数轴上有 N 家商店,它们的坐标分别为 A1∼AN。

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

输入格式

第一行输入整数 N。

第二行 N 个整数 A1∼AN。

输出格式

输出一个整数,表示距离之和的最小值。

数据范围

1≤N≤100000,
0≤Ai≤40000

输入样例:

4
6 2 9 1

输出样例:

12

思路:

要把货场建在某一个商店的位置上才是对所有情况的最优解。 

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 100005;
int main()
{
	int a[N];
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) cin >> a[i];
	sort(a, a + n);
	int mid = a[n / 2];
	long long int len = 0;
	for (int i = 0; i < n; i++)
	{
		len += abs(mid - a[i]);
	}
	cout << len;
}

125.耍杂技的牛

原题:

农民约翰的 N 头奶牛(编号为 1..N)计划逃跑并加入马戏团,为此它们决定练习表演杂技。

奶牛们不是非常有创意,只提出了一个杂技表演:

叠罗汉,表演时,奶牛们站在彼此的身上,形成一个高高的垂直堆叠。

奶牛们正在试图找到自己在这个堆叠中应该所处的位置顺序。

这 N 头奶牛中的每一头都有着自己的重量 Wi 以及自己的强壮程度 Si。

一头牛支撑不住的可能性取决于它头上所有牛的总重量(不包括它自己)减去它的身体强壮程度的值,现在称该数值为风险值,风险值越大,这只牛撑不住的可能性越高。

您的任务是确定奶牛的排序,使得所有奶牛的风险值中的最大值尽可能的小。

输入格式

第一行输入整数 N,表示奶牛数量。

接下来 N 行,每行输入两个整数,表示牛的重量和强壮程度,第 i 行表示第 i 头牛的重量 Wi 以及它的强壮程度 Si。

输出格式

输出一个整数,表示最大风险值的最小可能值。

数据范围

1≤N≤50000,
1≤Wi≤10,000,
1≤Si≤1,000,000,000

输入样例:

3
10 3
2 5
3 3

输出样例:

2

 

思路:

        牛牛叠高高时,重量和能力之和越小的牛站在越上面,即为最优解。为什么呢?

        因为它重量小适合叠在最上面,能力最小也适合叠在最上面,双重buff加成,所以wei + pow最小的适合在最上面。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 500005;
int main()
{
	pair<int, int>a[N];
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int wei, pow;
		cin >> wei >> pow;
		a[i] = { wei + pow,wei };
	}
	sort(a, a + n); // 对二元数组排序时,默认按第一个元素升序排序。
	int sum = 0,ass=-1e9; // sum为上面牛的重量之和,ass为风险值
	for (int i = 0; i < n; i++) 
	{
		int weig = a[i].second;
		int powe = a[i].first - weig;
		ass = max(ass, sum - powe);
		sum += weig;
	}
	cout << ass;
}


 

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值