1.区间选点(区间问题·贪心)
思路:
①将每个区间按右端点进行排序
②从前往后依次遍历每个区间
③如果当前区间已经被点覆盖,则pass,否则选择区间的最右边的点
④贪心的思想:每次选择局部最优点解,总体达到全部最优解(只有当问题是单峰的情况才可以使用贪心算法)
#include <bits/stdc++.h> using namespace std; typedef pair<int,int> PII; vector<PII> ports; bool cmp(PII a,PII b) //按pair的second排序 { return a.second < b.second; } int main() { int n; cin>>n; for(int i=0;i<n;i++) { int l,r; scanf("%d%d",&l,&r); ports.push_back({l,r}); } sort(ports.begin(),ports.end(),cmp); int ans=0,ed=-2e9; for(auto item : ports) if(item.first>ed) { ans++; ed=item.second; } cout<<ans; return 0; }
2.区间分组(区间问题·贪心)
思路:
①所有区间按照左端点从小到大排序
②从前往后处理每个区间,对于每个区间,判断能否将其放到某个组中(L[i] <= Max_r)
不存在这样的组,开辟一个新的组;存在这样的组,放在这个组中,并且更新当前组的Max_r
#include <bits/stdc++.h> using namespace std; const int N = 100010; struct Range { int l,r; }range[N]; bool cmp(Range a,Range b) //结构体自定义排序 { return a.l<b.l; } int main() { int n; cin>>n; for(int i=0;i<n;i++) { int l,r; scanf("%d%d",&l,&r); range[i]={l,r}; } sort(range,range+n,cmp); 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(); return 0; }
3.区间覆盖(区间问题·贪心)
输入样例:
1 5 3 -1 3 2 4 3 5
输出样例:
2
思路:
①将区间按左端点从小到大排序
②从前往后依次枚举每个区间,在所有能覆盖线段开头start的区间中选择一个右端点最大的
选完后将start更新成右端点的最大值
#include <bits/stdc++.h> using namespace std; const int N = 100010; struct Range { int l,r; }range[N]; bool cmp(Range a,Range b) { return a.l<b.l; } int main() { int st,ed; cin>>st>>ed; int n; cin>>n; for(int i=0;i<n;i++) { int l,r; scanf("%d%d",&l,&r); range[i]={l,r}; } sort(range,range+n,cmp); int res=0; bool flag=false; for(int i=0;i<n;i++) { int j=i,r=-2e9; while(j<n && range[j].l<=st) { r=max(r,range[j].r); j++; } res++; if(r<st) break; if(r>=ed) { flag=true; break; } i=j-1; st=r; } if(flag) cout<<res; else cout<<-1; return 0; }
4.耍杂技的牛(权重相加问题·贪心)
数据范围
1≤N≤50000, 1≤Wi≤10,000, 1≤Si≤1,000,000,000
输入样例:
3 10 3 2 5 3 3
输出样例:
2
思路:
按照wi+si从小到大排序排,结果一定是最优解
#include <bits/stdc++.h> using namespace std; const int N = 50010; struct Cow { int w,s,total; }cow[N]; bool cmp(Cow a,Cow b) { return a.total<b.total; } int main() { int n; cin>>n; for(int i=0;i<n;i++) { int w,s; scanf("%d%d",&w,&s); cow[i]={w,s,w+s}; } sort(cow,cow+n,cmp); int ans=-2e9,sum=0; for(int i=0;i<n;i++) { ans=max(ans,sum-cow[i].s); sum+=cow[i].w; } cout<<ans; return 0; }
5.合并果子(Huffman树)
在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。
达达决定把所有的果子合成一堆。
每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。
可以看出,所有的果子经过 n−1 次合并之后,就只剩下一堆了。
达达在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以达达在合并果子时要尽可能地节省体力。
假定每个果子重量都为 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使达达耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种果子,数目依次为 1,2,9。
可以先将 1、2 堆合并,新堆数目为 3,耗费体力为 3。
接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12。
所以达达总共耗费体力=3+12=15。
可以证明 15 为最小的体力耗费值。
输入样例:
3 1 2 9
输出样例:
15
每次选权重最小的两堆合并,结果就是全局最优解
#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 a; scanf("%d",&a); heap.push(a); } int ans=0; while(heap.size()>1) { int a=heap.top(); heap.pop(); int b=heap.top(); heap.pop(); ans+=a; ans+=b; heap.push(a+b); } cout<<ans; return 0; }
6.排队打水(排序不等式)
输入样例:
7 3 6 1 4 2 5 7
输出样例:
56
排序相加策略:
#include <bits/stdc++.h> using namespace std; const int N = 100010; int a[N]; int main() { int n; cin>>n; for(int i=0;i<n;i++) scanf("%d",&a[i]); sort(a,a+n); long long ans=0; for(int i=0;i<n;i++) ans+=a[i]*(n-i-1); cout<<ans; return 0; }
7.货舱选址(绝对值不等式)
输入样例:
4 6 2 9 1
输出样例:
12
各个货舱的点距离所有货舱的中位数距离最近:
#include <bits/stdc++.h> using namespace std; const int N = 100010; int a[N]; int main() { int n; cin>>n; for(int i=0;i<n;i++) scanf("%d",&a[i]); sort(a,a+n); long long ans=0; for(int i=0;i<n;i++) ans+=abs(a[i]-a[n/2]); cout<<ans; return 0; }