C++刷题 - 7.27

贪心算法的详细逻辑

这个问题的最优解可以用 贪心算法O(N) 时间 内解决。它的核心思想是:

每次操作尽可能覆盖最长的连续非零区间 ,并通过数学分析发现:最小操作次数等于所有"上升台阶"的高度差之和


1. 直观理解

假设 steps = [1, 2, 3, 2, 1],我们可以这样操作:

  1. 第一次操作 :覆盖整个数组 [1,2,3,2,1][0,1,2,1,0]

    (操作次数 +=1)

  2. 第二次操作 :覆盖 [1,2,1][0,0,1,0,0]

    (操作次数 +=1)

  3. 第三次操作 :覆盖 [1][0,0,0,0,0]

    (操作次数 +=1)

总操作次数 = 3 ,正好等于 1 (初始) + (2-1) + (3-2) = 3


2. 贪心策略的数学证明

  • 关键观察

    • 如果当前数字 steps[i] 比前一个数字 steps[i-1] 大,说明需要 新增 steps[i] - steps[i-1] 次操作

    • 如果 steps[i] <= steps[i-1],说明它可以被之前的操作覆盖,无需新增操作次数。

  • 数学表达

    text

    复制代码
    总操作次数 = steps[0] + Σ max(steps[i] - steps[i-1], 0)  (i = 1 to N-1)

3. 代码实现

cpp

复制代码
#include <vector>
#include <iostream>
using namespace std;

int GetMinStep(const vector<int>& steps) {
    if (steps.empty()) return 0;
    int res = steps[0]; // 初始必须有 steps[0] 次操作
    for (int i = 1; i < steps.size(); i++) {
        if (steps[i] > steps[i-1]) {
            res += steps[i] - steps[i-1]; // 增加新的操作
        }
    }
    return res;
}

int main() {
    vector<int> steps1 = {1, 2, 3, 2, 1};
    cout << GetMinStep(steps1) << endl; // 输出 3

    vector<int> steps2 = {3, 2, 1, 2, 3};
    cout << GetMinStep(steps2) << endl; // 输出 5

    vector<int> steps3 = {4, 1, 2, 3, 1};
    cout << GetMinStep(steps3) << endl; // 输出 6 (4 + 0 + 1 + 1 + 0)

    return 0;
}

4. 为什么贪心算法是正确的?

反证法

假设贪心算法不是最优的,那么存在某个 steps[i] 没有被正确覆盖:

  • 情况 1steps[i] > steps[i-1]

    如果少操作,会导致 steps[i] 无法归零,矛盾。

  • 情况 2steps[i] <= steps[i-1]

    它已经被之前的操作覆盖,无需额外操作。

因此,贪心策略的正确性得证。


5. 对比原始模拟方法

方法 时间复杂度 适用场景
原始模拟(每次找最长区间减1) O(N²) 直观但较慢
贪心算法(数学优化) O(N) 最优解

6. 实际应用示例

示例 1steps = [3, 2, 1, 2, 3]
  • 计算过程

    • res = 3(初始)

    • i=1: 2 <= 3 → 不增加

    • i=2: 1 <= 2 → 不增加

    • i=3: 2 > 1res += 1res=4

    • i=4: 3 > 2res += 1res=5

  • 总操作次数 = 5

示例 2steps = [4, 1, 2, 3, 1]
  • 计算过程

    • res = 4(初始)

    • i=1: 1 <= 4 → 不增加

    • i=2: 2 > 1res += 1res=5

    • i=3: 3 > 2res += 1res=6

    • i=4: 1 <= 3 → 不增加

  • 总操作次数 = 6


7. 总结

  • 贪心策略:利用高度差直接计算最小操作次数。

  • 优势:O(N) 时间,无需模拟每次操作。

  • 适用场景:题目允许数学分析时,优先使用贪心算法。

这种方法高效且优雅,能完美解决问题! 🎯


资源

cpp 复制代码
#include <iostream>
#include <vector>
#include <climits>
#include <queue>
#include <list>
#include <unordered_set>
using namespace std;
void PrintList(list<pair<int,int>> lists)
{
    for(auto l:lists)

    {
        cout<<"<"<<l.first<<" "<<l.second<<">"<<"   ";
    }
    cout<<endl;
}

int Solution(const int frameNum, const int windowSize, const vector<int> pages) 
{
    int ret = 0;
    // 记录访问次数,访问时间,
    // <51 1> <52 1> <53 1>
    // <52 1> <53 1> <54 1>
    // 要么遍历一遍时间,要么遍历一遍page,要么加空间
    // 链表比较合适,
    list<pair<int,int>> pagesList;
    for(auto& page:pages)
    {
        auto it = pagesList.begin();
        bool flagFind = false;
        while(it!=pagesList.end())
        {
            if(it->first == page)
            {
                flagFind = true;
                it->second++;
                break;
            }
            it++;
        }
        if(flagFind == false)
        {
            if(pagesList.size()<frameNum)
            {
                pagesList.push_back(make_pair(page,1));
            }
            else // 触发置换
            {
                //从头节点开始取windowSize个
                //遍历得到最小值
                auto left = pagesList.begin();
                int nums = windowSize;
                int minof = INT_MAX;

                while (nums>0)
                {
                    minof = min(minof,left->second);
                    left++;
                    nums--;
                }
                nums = windowSize;
                left = pagesList.begin();
                while (nums>0)
                {
                    if(left->second==minof)
                    {
                        pagesList.erase(left);
                        pagesList.push_back(make_pair(page,1));
                        break;
                    }
                    left++;
                    nums--;
                }
                ret++;
            }
        }
        //PrintList(pagesList);
        //cout<<ret<<endl;
    }
    return ret;
}

单词统计

cpp 复制代码
int Solution(const vector<string> lines) 
{
    // 需要特殊处理的 " " . ,    -
    string allLines;
    int ret = 0;
    for(int l=0;l<lines.size();l++)
    {
        int i =0;
        if(lines[l] == "-")
        {  
                
        }    
        else
        {  
            while (i<lines[l].size())
            {

                while(i<lines[l].size()&&(lines[l][i]==','||lines[l][i]=='.'||lines[l][i]==' '))
                {
                    i++;
                }
                if(i<lines[l].size())
                {
                    ret++;
                    while((i<lines[l].size())&&(lines[l][i]<='z'&&lines[l][i]>='a'))
                    {
                        i++;
                    } 
                }
                if((i==lines[l].size()-1 )&& lines[l][i]=='-') 
                {
                    i++;
                    if(l+1<lines.size()&&lines[l+1][0]>='a'&&lines[l+1][0]<='z')   
                    { 
                        ret--;
                    }
                    if(l+1<lines.size()&&lines[l+1]=="-")
                    {
                        ret--;
                    }
                }
            }
            
        }
    }
    return ret;
}

cpp 复制代码
#include <iostream>
#include <vector>
#include <climits>
#include <queue>
#include <list>
#include <unordered_map>
#include <map>
#include <algorithm>


using namespace std;

struct  process
{
    string _appName;
    string _userName;
    int _cpuUsed;
    int _memUsed;
};


//应用进程统计
vector<string> Solution(const vector<process> processes,const vector<string> sortRules,const vector<string> selectedUsers)
{
    vector<string> ret;
    // 规则1 按照selectedUsers筛选
    vector<process> processAfterSelect;
    for(auto& process : processes)
    {
        for(auto& e:selectedUsers)
        {
            if(e==process._userName)
            {
                processAfterSelect.push_back(process);
                break;
            }
        }
    }
    // 按照_appName进行累加
    unordered_map<string, process> processAfterAdd;
    for(auto& process : processAfterSelect)
    {
        if(processAfterAdd.count(process._appName)!=0)
        {
            processAfterAdd[process._appName]._cpuUsed+=process._cpuUsed;
            processAfterAdd[process._appName]._memUsed+=process._memUsed;

        }
        else
        {
            processAfterAdd[process._appName]._appName=process._appName;
            processAfterAdd[process._appName]._cpuUsed=process._cpuUsed;
            processAfterAdd[process._appName]._memUsed=process._memUsed;
        }
    }

    // 按照sortRules 和 appName 排序
    processAfterSelect.clear();
    for(auto& process:processAfterAdd)
    {
        processAfterSelect.push_back(process.second);
    }

    // 按照排序字段
    sort(processAfterSelect.begin(),processAfterSelect.end(),[&sortRules](const process& a,const process& b)
    {
        if(sortRules.size() == 0)
        {
            return a._appName > b._appName;
        }
        else if(sortRules.size() == 1)
        {
            if(sortRules[0] == "cpuUsed" && a._cpuUsed!=b._cpuUsed)
            {
                return a._cpuUsed > b._cpuUsed;
            }
            else if(sortRules[0] == "cpuUsed" && a._cpuUsed==b._cpuUsed)
            {
                return a._appName > b._appName;
            }
            else if(sortRules[0] == "memUsed" && a._memUsed!=b._memUsed)
            {
                return a._memUsed > b._memUsed;
            }            
            else
            {
                return a._appName > b._appName;
            }
        }
        else
        {
            if(sortRules[0] == "cpuUsed" && sortRules[0] == "memUsed" )
            {
               if(a._cpuUsed!=b._cpuUsed)
               {
                    return a._cpuUsed > b._cpuUsed;
               }
               else if(a._cpuUsed==b._cpuUsed && a._memUsed!=b._memUsed)
               {
                    return a._memUsed > b._memUsed;
               }
               else
               {
                    return a._appName > b._appName;
               }
            }
            else 
            {
               if(a._memUsed!=b._memUsed)
               {
                    return a._memUsed > b._memUsed;
               }
               else if(a._memUsed==b._memUsed && a._cpuUsed!=b._cpuUsed)
               {
                    return a._cpuUsed > b._cpuUsed;
               }
               else
               {
                    return a._appName > b._appName;
               }
            }
        }
    });
    int length = 3>processAfterSelect.size()? processAfterSelect.size():3;

    auto start = processAfterSelect.begin();
    while(length>0)
    {
        ret.push_back(start->_appName);
        start++;
        length--;
    }
    return ret;
};

int main() {
    
    vector<process> processes = {{"Explorer","user1",6,1000},{"chrome","user1",3,800},{"chrome","user1",4,200},{"welink","user1",9,800},{"edge","user1",8,900},{"app","ur1",4,200}};
    vector<string> selectedUsers = { "user1"};
    vector<string> sortRules = { "memUsed","cpuUsed"};

    vector<string> ret = Solution(processes,sortRules,selectedUsers);
    for(auto e:ret)
    {
        cout<<e<<endl;
    }
    return 0;
}
相关推荐
lifallen2 小时前
深入解析RocksDB的MVCC和LSM Tree level
大数据·数据结构·数据库·c++·lsm-tree·lsm tree
鹿野素材屋2 小时前
C#中对于List的多种排序方式
开发语言·c#
君鼎2 小时前
Effective C++ 条款18:让接口容易被正确使用,不易被误用
c++
白日梦想家-K3 小时前
题单【模拟与高精度】
开发语言·c++·算法
岁忧3 小时前
(LeetCode 面试经典 150 题) 138. 随机链表的复制 (哈希表)
java·c++·leetcode·链表·面试·go
鹦鹉0073 小时前
IO流中的字节流
java·开发语言·后端
重生之我是Java开发战士3 小时前
【C语言】内存函数与数据在内存中的存储
c语言·开发语言·算法
君鼎3 小时前
Effective C++ 条款17:以独立语句将newed对象置入智能指针
c++
haaaaaaarry3 小时前
Element Plus常见基础组件(二)
开发语言·前端·javascript