题目和模板见第一讲
acwing算法基础课第六讲:贪心,包括区间问题,Huffman树,排序不等式,绝对值不等式,推公式等内容。
区间问题
905.区间选点
数轴上有一些区间,在数轴上选取几个点,要求每个区间上最少有一个点。
贪心跟排序往往离不开,所以其中一个核心问题是确定如何排序。这里要按照右端点排序,因为贪心每次选择的是最右的端点!!!
int main(){
cin>>n;
vector<vector<int>>kk;
for(int i=0;i<n;i++){
cin>>a>>b;
kk.push_back({a,b});
}
sort(kk.begin(),kk.end(),[](const vector<int>&p,const vector<int>&q){
return p[1]<q[1];
});
int ans=1;int t=kk[0][1];
for(int i=0;i<n;i++){
if(t>=kk[i][0])continue;
else{
ans++;
t=kk[i][1];
}
}
cout<<ans<<endl;
return 0;
}
908.最大不相交区间

分析一下就可以发现,代码跟上面一模一样。
由于区间左端点天然小于等于右端点的特性,所以右端点为基准的排序为宜
906.区间分组

很好很好的题!转述一下,就是经典的活动安排问题:


所以这里开一个数组同时存开始和结束时间,并对其排序,如果开始cur+1,结束cur-1
注意一下这里判断用if和else就可以,不用elseif 因为可能会导致负数情况的判断错误
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int store[N*2],idx;
int main(){
int n,a,b;cin>>n;
for(int i=0;i<n;i++){
cin>>a>>b;
store[idx++]=a*2;//偶数存开始
store[idx++]=b*2+1;//奇数存结束
}
sort(store,store+idx);
int cur=0;int ans=1;
for(int i=0;i<idx;i++){
if(store[i]%2==0)cur+=1;
else cur-=1;
ans=max(ans,cur);
}
cout<<ans<<endl;
return 0;
}
907.区间覆盖
反正记住,这类涉及区间问题的,都是贪心的做法。这里的贪心就是把区间按左端点排序,然后每次选择右边伸出去最长的。
#include <bits/stdc++.h>
using namespace std;
int main() {
int s, t, n;
cin >> s >> t >> n;
vector<pair<int,int>> segs(n);
for (int i = 0; i < n; i++) {
cin >> segs[i].first >> segs[i].second;
}
// 严格按左端点升序排序
sort(segs.begin(), segs.end(), [](auto &A, auto &B){
return A.first < B.first;
});
// 特殊情况:目标区间为单点 [s, s]
if (s == t) {
bool found = false;
for (auto &seg : segs) {
if (seg.first <= s && seg.second >= t) {
found = true;
break;
}
}
cout << (found ? 1 : -1) << "\n";
return 0;
}
int ans = 0;
int i = 0;
// 贪心:每次在当前可覆盖范围内选一个能把右端延伸最远的区间
while (i < n && s < t) {
int furthest = s;
while (i < n && segs[i].first <= s) {
furthest = max(furthest, segs[i].second);
i++;
}
if (furthest == s) {
// 无法继续延伸,覆盖失败
cout << -1 << "\n";
return 0;
}
s = furthest;
ans++;
}
// 检查是否完全覆盖到 t
if (s < t) {
cout << -1 << "\n";
} else {
cout << ans << "\n";
}
return 0;
}
Huffman树
148.合并果子
哈夫曼树是优先队列(小跟堆)的经典使用了,再次复习下小跟堆的语法:
包含在头文件queue中;
priority_queue<int> pq; // 默认大根堆, 即每次取出的元素是队列中的最大值 priority_queue<int, vector<int>, greater<int>> q; // 小根堆, 每次取出的元素是队列中的最小值

也可以通过struct结构定义operateor:

堆本质上是队列,所以stl和队列一样:push,top,pop,size四大天王。
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int n,x;
int main(){
cin>>n;priority_queue<int,vector<int>,greater<int>>pq;
int ans=0;
for(int i=0;i<n;i++){
cin>>x;
pq.push(x);
}
int i=0;
while(i<n-1){
int a=pq.top();pq.pop();
int b=pq.top();pq.pop();
ans+=(a+b);
pq.push(a+b);
i++;
}
cout<<ans<<endl;
return 0;
}
排序不等式
913.排队打水

排个序就行,让时间越短的人越先打水 注意typdef开 long long
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e+5;
int wa[N];
int main(){
int n;cin>>n;
for(int i=0;i<n;i++)cin>>wa[i];
sort(wa,wa+n);
long int ans=0;long int t=0;
for(int i=0;i<n;i++){
ans+=t;
t+=(long int)wa[i];
}
cout<<ans<<endl;
return 0;
}
绝对值不等式
104.货仓选址

排个序,选中间的。
绝对值不等式?∣a∣+∣b∣≥∣a+b|
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e5+5;
typedef long long LL;
int main(){
int n;cin>>n;int a;
vector<int>po;
for(int i=0;i<n;i++){
cin>>a;po.push_back(a);
}
sort(po.begin(),po.end());
int le=0;int ri=po.size()-1;
LL ans=0;
while(le<ri){
ans+=(LL)(po[ri]-po[le]);
ri--;le++;
}
cout<<ans<<endl;
return 0;
}
推公式
125.耍杂技的牛

确实是推公式,写一下i和i+1处风险值的最大值,就可以发现。
要使每头牛的危险值最小,根据贪心思想:
自身w值越大应该放到底部(即减小上述式中的被减数)
自身s值越大应该放到底部(即增大上述式中的减数)
可以得到w+s越小的牛在上面会更好(或者说题做多了一眼就可以看出来)
于是升序排序就行。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long int LL;
int main(){
int n,w,s;cin>>n;
vector<pair<int,int>>st;
for(int i=0;i<n;i++){
cin>>w>>s;
st.push_back(make_pair(w,s));
}
sort(st.begin(),st.end(),[](const pair<int,int>&a,const pair<int,int>&b){
return a.first+a.second<b.first+b.second;
});
LL ans=-1e9;LL sum=0;
for(int i=0;i<n;i++){
ans=max(ans,sum-st[i].second);
sum+=st[i].first;
}
cout<<ans<<endl;
return 0;
}
数学相关
875.快速幂
快速幂的核心是分治(递归),即将指数进行划分!
模板如下:
def fast_pow(a: float, n: int) -> float:
if n == 0:
return 1
elif n < 0: # 处理负指数
return 1 / fast_pow(a, -n)
half = fast_pow(a, n // 2)
if n % 2 == 0:
return half * half
else:
return a * half * half
对于本题:注意对a取模,还有一个点很重要!——分步取模!
if(b&1) return res*res%p*a%p;
如果是(half*half*a)%c;可能会在中间部分溢出!
#include<iostream>
using namespace std;
const int N=1e5+5;
typedef long long LL;
LL qpow(LL a,LL b,LL p)
{
if(b==0) return 1;
a%=p;
LL res=qpow(a,b>>1,p);
if(b&1) return res*res%p*a%p;
return res*res%p;
}
int main(){
int n;cin>>n;LL a,b,c;
for(int i=0;i<n;i++){
cin>>a>>b>>c;
cout<<qpow(a,b,c)<<endl;
}
return 0;
}
876.快速幂求逆元
什么是逆元?——由于除法实现复杂,我们希望在mod m的情况下,a/b和a*x等价,即把除法变成乘法,我们把x叫做b的modm的逆元,记为b-1 再换而言之,b*x=1(mod m)

求逆元要用到费马小定理:

把a^p-1拆成a*a^p-2,所以a^p-2即为a的逆元
什么时候无解? 如果a是p的倍数,显然mod p为0就不是解,所以需要特判。
#include <iostream>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while(b){
if(b & 1) res = res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
int main()
{
int n; cin >> n;
while(n --){
int a, p;
cin >> a >> p;
if(a % p == 0) puts("impossible");
else cout << qmi(a, p - 2, p) << endl;
}
return 0;
}
643

被折叠的 条评论
为什么被折叠?



