算法基础课之贪心算法
1. 区间选点
题解:
算法思想:
- 现将所有区间存在定义的结构体数组中
- 按照最右端点从小到排序
- 再设置一个最小点 ed=-2e9
- 当最小点ed不在区间里时,ed变为该区间的右端点,计数res+1
算法复杂度为(nlongn)
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l,r;
bool operator< (const Range &W)const
{
return r<W.r;
}
}range[N]; //用于存放n个区间
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>range[i].l>>range[i].r;
}
sort(range,range+n);
int res=0,ed=-2e9;
for(int i=0;i<n;i++)
{
if(ed<range[i].l)
{
ed=range[i].r;
res++;
}
}
cout<<res<<endl;
return 0;
}
2. 最大不相交数量
题解:
最大区间不相交数量这一题类似于
小明在一天中有n个活动可供参加,每个活动都有开始时间和结束时间,请据此选择小明最多可参加的活动数量
算法思想和区间选点相同
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l,r;
bool operator< (const Range &W)const
{
return r<W.r;
}
}range[N]; //用于存放n个区间
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>range[i].l>>range[i].r;
}
sort(range,range+n);
int res=0,ed=-2e9;
for(int i=0;i<n;i++)
{
if(ed<range[i].l)
{
ed=range[i].r;
res++;
}
}
cout<<res<<endl;
return 0;
}
3. 区间分组
题解:
STL中的堆的定义方法是
priority_queue[HTML_REMOVED]> 默认为大根堆
若要变为小根堆要在后面加上greater[HTML_REMOVED]
小根堆栈顶元素top()为栈中最小值
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range
{
int l,r;
bool operator< (const Range &W)const
{
return l<W.l;
}
}range[N];
int main()
{
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++)
{
if(heap.empty()||heap.top()>=range[i].l)
{
heap.push(range[i].r);
}
else{
heap.pop();
heap.push(range[i].r);
}
}
cout<<heap.size()<<endl;
return 0;
}
4. 区间覆盖
题解:
算法思想:
- 把所有区间按左端点从小到大排序
- 找出l<=st的所有区间的右端点的最大值r(用双指针算法)
- 将r赋值给st,直到r>=ed
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
struct Range{
int l,r;
bool operator< (const Range &W)const
{
return l<W.l;
}
}range[N];
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int st,ed;
cin>>st>>ed;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>range[i].l>>range[i].r;
}
sort(range,range+n);
int res=0; //用来记录最少区间数
bool flag=false;
for(int i=0;i<n;i++)
{
//双指针算法,找出所有l<=st的区间中,r最大的
int j=i,r=-2e9;
while(j<n&&range[j].l<=st)
{
r=max(r,range[j].r);
j++;
}
// 情况1:再所有满足l<=st的区间中,最大的r还是小于st
// 这种情况,不用再将r赋值给st了,直接跳出循环
if(r<st)
{
flag=true;
break;
}
res++;
// 情况2:r>=ed,直接第一次就找到了全覆盖的区间,也跳出循环
if(r>=ed)
{
break;
}
st=r;
i=j-1;
if(j==n)
{
flag=true;
break;
}
}
if(flag) cout<<-1<<endl;
else cout<<res<<endl;
return 0;
}
5. 合并果子
题解:
算法思想,其实这题类似于石子合并问题
利用哈夫曼数,合并最小两数的思想,将所有数放在小根堆中,堆顶元素top为最小值,当堆中元素大于1时,两个最小元素相加,然后计算和,出堆,和入堆
#include<bits/stdc++.h>
using namespace std;
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 res=0;
while(heap.size()>1)
{
int a = heap.top(); heap.pop();
int b = heap.top();heap.pop();
res+=(a+b);
heap.push(a+b);
}
cout<<res<<endl;
return 0;
}
6. 排队打水
题解:
等待时间最小的放在前面,先打水
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
int a[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);
LL res=0;
for(int i=0;i<n;i++)
{
res+=(a[i]*(n-i-1));
}
cout<<res<<endl;
return 0;
}
7. 仓货选址
题解:
算法思想:
排完序只有找中位数,放在中位数上里各个点的距离最小
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N];
int n;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);
int res=0;
for(int i=0;i<n;i++)
{
res+=(abs(a[i]-a[n/2]));
}
cout<<res<<endl;
return 0;
}
8. 耍杂技的牛
题解:
算法思想:
根据w+s的和排序,最小的牛站在最上面
#include<bits/stdc++.h>
using namespace std;
const int N = 50010;
typedef long long LL;
typedef pair<int, int> PII;
PII a[N];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
int w,s;
cin>>w>>s;
a[i].first=(w+s);
a[i].second=s;
}
sort(a,a+n);
LL sum=0,res=-2e18;
for(int i=0;i<n;i++)
{
sum-=a[i].second;
res=max(res,sum);
sum+=a[i].first;
}
cout<<res<<endl;
return 0;
}