目录
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 来源: 算法标签
代码:
#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;
}