贪心算法的详细逻辑
这个问题的最优解可以用 贪心算法 在 O(N) 时间 内解决。它的核心思想是:
每次操作尽可能覆盖最长的连续非零区间 ,并通过数学分析发现:最小操作次数等于所有"上升台阶"的高度差之和。
1. 直观理解
假设 steps = [1, 2, 3, 2, 1]
,我们可以这样操作:
-
第一次操作 :覆盖整个数组
[1,2,3,2,1]
→[0,1,2,1,0]
(操作次数 +=1)
-
第二次操作 :覆盖
[1,2,1]
→[0,0,1,0,0]
(操作次数 +=1)
-
第三次操作 :覆盖
[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]
没有被正确覆盖:
-
情况 1 :
steps[i] > steps[i-1]
如果少操作,会导致
steps[i]
无法归零,矛盾。 -
情况 2 :
steps[i] <= steps[i-1]
它已经被之前的操作覆盖,无需额外操作。
因此,贪心策略的正确性得证。
5. 对比原始模拟方法
方法 | 时间复杂度 | 适用场景 |
---|---|---|
原始模拟(每次找最长区间减1) | O(N²) | 直观但较慢 |
贪心算法(数学优化) | O(N) | 最优解 |
6. 实际应用示例
示例 1 :steps = [3, 2, 1, 2, 3]
-
计算过程:
-
res = 3
(初始) -
i=1
:2 <= 3
→ 不增加 -
i=2
:1 <= 2
→ 不增加 -
i=3
:2 > 1
→res += 1
→res=4
-
i=4
:3 > 2
→res += 1
→res=5
-
-
总操作次数 = 5
示例 2 :steps = [4, 1, 2, 3, 1]
-
计算过程:
-
res = 4
(初始) -
i=1
:1 <= 4
→ 不增加 -
i=2
:2 > 1
→res += 1
→res=5
-
i=3
:3 > 2
→res += 1
→res=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;
}