Acwing算法刷题记录——第六讲

题目和模板见第一讲

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值越大应该放到底部(即增大上述式中的减数)

证明:AcWing 125. 耍杂技的牛 - AcWing

可以得到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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值