SCAU算法设计与分析 —— 贪心算法

3 阅读7分钟

By 三石吖 2026.2

4.贪心算法

11091 最优自然数分解问题

(1)容易想到肯定是先拆成连续的自然数,然后如果有剩余就按照从大到小的顺序给已经选中的数+1+1

(2)看提示做的,很困难啊

  • 时间复杂度O(n)O(n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void get1(int n){
    std::vector<int>a;
    for(int i = 2; i <= n; i++){
        a.push_back(i);
        n -= i;
    }
    if(n){
        for(int i = a.size() - 1; i >= 0 && n; i--){
            a[i]++;
            n--;
        }
    }
    i64 res = 1;
    for(auto c : a){
        res *= c;
    }
    std::cout << res << " ";
}

void get2(int n){
    i64 res2 = 1;
    while(n >= 3){
        res2 *= 3;
        n -= 3;
    }
    if(n == 1){
        res2 = res2 / 3 * 4;
    }
    while(n >= 2){
        res2 *= 2;
        n -= 2;
    }
    std::cout << res2 << "\n";
}

void solve(){
    int n;
    std::cin >> n;
    if(n <= 2){
        std::cout << n << " " << n << "\n";
        return;
    }else if(n == 3){
        std::cout << 2 << " " << 3 << "\n";
        return;
    }

    get1(n), get2(n);
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

8598 整除15问题

不想重写了,就是讨论讨论判判判

端了个去年十月写的版本上来

  • 时间复杂度:O(n)O(n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    std::string s;
    std::cin >> s;
    std::vector<int>cnt(10, 0);
    i64 sum = 0;
    for(auto c : s){
        cnt[c - '0']++;
        sum += (c - '0');    
    }

    if(sum % 3 == 2){
        for(int i = 2; sum % 3 && i <= 9; i += 3){
            if(i == 5 && cnt[i] == 1 && !cnt[0]){
                continue;
            }
            if(cnt[i]){
                cnt[i]--;
                sum -= i;
            }
        }
        for(int i = 1; sum % 3 && i <= 9; i += 3){
            while(cnt[i] && sum % 3){
                cnt[i]--;
                sum -= i;
            }
        }
    }else if(sum % 3 == 1){
        for(int i = 1; sum % 3 && i <= 9; i += 3){
            if(cnt[i]){
                cnt[i]--;
                sum -= i;
            }
        }
        for(int i = 2; sum % 3 && i<= 9; i += 3){
            while(cnt[i] && sum % 3){
                if(i == 5 && cnt[i] == 1 && !cnt[0]){
                    break;
                }
                cnt[i]--;
                sum -= i;
            }
        }
    }
    if(sum % 3 || count(cnt.begin(), cnt.end(), 0)==10 || (!cnt[0] && !cnt[5])){
        std::cout << "impossible\n";
    }else{
        if(cnt[0]){
            for(int i = 9; i >= 0; i--){
                for(int j = 0; j < cnt[i]; j++){
                    std::cout << i;
                }
            }
        }else{
            for(int i = 9; i >= 0; i--){
                for(int j = 0; j < (i == 5 ? cnt[i] - 1:cnt[i]); j++){
                    std::cout << i;
                }
            }
            std::cout << 5;
        }
        std::cout << "\n";
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

8602 区间相交问题

按右边界排序,数有多少个不重的区间

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    std::vector<pii>a(n);
    for(int i = 0; i < n; i++){
        std::cin >> a[i].second >> a[i].first;
    }
    std::sort(a.begin(), a.end());
    int end = -inf;
    int cnt = 0;
    for(int i = 0; i < n; i++){
        if(a[i].second >= end){
            cnt++;
            end = a[i].first;
        }
    }
    std::cout << n - cnt << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

8605 删数问题

暴力跑kk轮,每轮从高位往低位贪心,删第一个减小的位置

  • 时间复杂度O(k×n)O(k \times n)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    std::string s;
    int k;
    while(1){
        std::cin >> s;
        if(s == "0"){
            break;
        }
        std::cin >> k;
        for(int i = 0; i < k; i++){
            int mx = s[0] - '0';
            int inx = 0;
            int n = s.size();
            for(int j = 1; j < n; j++){
                if(s[j] - '0' < mx) {
                    break;
                }
                mx = s[j] - '0';
                inx = j;
            }
            s.erase(s.begin() + inx);
        }
        std::cout << s << "\n";
    }
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

10346 带限期和价值的作业安排问题

按价值排序,然后记录一下有没有用过这个时间点

  • 时间复杂度O(nlgn)O(nlgn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    std::vector<std::vector<int>>a(n, std::vector<int>(2));
    for(int i = 0; i < n; i++){
        std::cin >> a[i][0];
    }
    for(int i = 0; i < n; i++){
        std::cin >> a[i][1];
    }
    std::sort(a.begin(), a.end(), [](const std::vector<int> &x, const std::vector<int> &y){
        return x[1] > y[1];
    });
    std::map<int, int>vis;
    int ans = 0;
    for(int i = 0; i < n; i++){
        if(vis[a[i][0]]){
            continue;
        }
        vis[a[i][0]] = 1;
        ans += a[i][1];
    }
    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

11079 可以移动的石子合并

(1)最高得分就是每次选最大的两堆,可以直接算数学关系

(2)最小得分就是每次选最小的kk堆,用小根堆模拟即可

  • 时间复杂度O(nlon)O(nlon)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n, k;
    std::cin >> n >> k;
    std::vector<int>a(n);
    std::priority_queue<int, std::vector<int>, std::greater<>>que;
    for(int i = 0; i < n; i++){
        std::cin >> a[i];
        que.push(a[i]);
    }

    std::sort(a.begin(), a.end(), std::greater<>());

    i64 max = 1LL * (n - 1) * a[0], min = 0;
    for(int i = 1; i < n; i++){
        max += 1LL * (n - i) * a[i];
    }
    while(n % (k - 1) != 1){
        que.push(0);
        n++;
    }

    while(que.size() > 1){
        i64 cur=0;
        int siz = que.size();
        for(int i = 0; i < std::min(k, siz); i++){
            cur += que.top();
            que.pop();
        }
        min += cur;
        que.push(cur);
    }
    std::cout << min << " " << max << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17964 水桶打水

按时间从小到大排序, 要让等待时间短,优先放打的快的,每次放入都选等待时间最短的那个水龙头,然后用小顶堆模拟

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n, r;
    std::cin >> n >> r;
    std::vector<int>t(n);
    for(int i = 0; i < n; i++){
        std::cin >> t[i];
    }

    std::sort(t.begin(), t.end());
    std::priority_queue<int, std::vector<int>, std::greater<>>que;
    
    for(int i = 0; i < r; i++){
        que.push(0);
    }

    int ans = 0;
    for(int i = 0; i < n; i++){
        int x = que.top();
        que.pop();
        que.push(x + t[i]);
        ans += x + t[i];
    }

    std::cout << ans << "\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/  

17103 基站建设

发现一个基站可以影响88公里,那么排序后按长度为88分块即可

  • 时间复杂度O(nlogn)O(nlogn)
#include<bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using pii = std::pair<int,int>;
using pll = std::pair<i64,i64>;
using pli = std::pair<i64,int>;
using pil = std::pair<int,i64>;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3fll;
constexpr int inf = 0x3f3f3f3f;

void solve(){
    int n;
    std::cin >> n;
    std::vector<int>a(n);
    for(int i = 0; i < n; i++){
        std::cin >> a[i];
    }
    std::sort(a.begin(), a.end());
    int ans = 0;

    for(int i = 0, j; i < n; i = j){
        ans++;

        j = i + 1;
        while(j < n && a[j] - a[i] <= 8){
            j++;
        }
    }
    std::cout<<ans<<"\n";
}             

int main(){ 
    std::ios::sync_with_stdio(false);    
    std::cin.tie(nullptr);

#ifdef LOCAL
    freopen("make.txt", "r", stdin);
    freopen("a.txt", "w", stdout);
#endif

    int T = 1;
    // std::cin >> T;
    while(T--){
        solve();
    }
    return 0; 
}   
/* 

*/