贪心算法
贪心的基本原理:每一步都选择局部最优解而尽量不考虑对后续的影响,最终达到全局最优解。
贪心的局限性:贪心算法不能保证获得全局最》解,但在某些问题上具有高效性。
贪心的特征:贪心选择性质()、最优子结构性质(根据我的观察,很多贪心的题目会出现"不同的操作产生的贡献相同"的特征,在此特征下我们每次选择代价最小的)。
贪心算法实现步骤:
1.确定问题的最优子结构贪心往往和排序、优先队列等一起出现)
"最小代价"。
2.构建贪心选择的策略,可能通过"分类讨论""最大价值"等方式来思考贪心策略。简单验证贪心的正确性,采用句!般是:这样做一定不会使得结果变差、不存在比当前方案更好的方案等等。
3.通过贪心选择逐步求解问题,直到得到最终解。
例题
分析:
简单排序模型。
要将战斗力分为两部分,为了使得差距最小,我们可以将战斗力排序后,找一个位置进行划分,使得左边的都在a,右边的都在b,从而这个差距就是这个位置两边的战斗力差距,说明差距的取值仅有n-1种,枚举即可。
这个题启发我们,当混乱的数据不好处理且排序不影响答案时,尝试先排序再分析
代码如下:
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
int ans=a[2]-a[1];
for(int i=1;i<n;i++)
{
ans=min(ans,a[i+1]-a[i]);
}
cout<<ans;
return 0;
}
分析:
总操作数一定情况下的最小代价模型。
我们知道这里一共需要进行的操作次数一定是n-1次,那么贪心地想,如果
每次选择代价最小的两个部落合并,不仅可以使得当前代价最小,还可以使得后续合并的代价也尽可能小。部落大小通过优先队列来维护。
代码如下:
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
// 使用 lambda 函数作为比较器
auto com = [](int a, int b) {
return a > b; // 小顶堆
};
// 定义优先队列,使用自定义的比较器
priority_queue<int, vector<int>, decltype(com)> pq(com);
// 输入数据并将其推入优先队列
for (int i = 1; i <= n; i++) {
int x;
cin >> x; // 读取输入
pq.push(x);
}
int sum = 0; // 初始化 sum
// 合并过程
while (pq.size() > 1) { // 确保有至少两个元素
int y = pq.top();
pq.pop();
int z = pq.top(); // 获取下一个元素
pq.pop();
int ans = y + z; // 合并
pq.push(ans); // 将合并后的值推入优先队列
sum += ans; // 累加到 sum
}
cout << sum << endl; // 输出结果
return 0;
}
分析:
最少数目的贪心模型。
为了使得组数最小,我们应该使得每一组内尽可能装两件礼物(最多只能装两件),尽量占满一组的容量。所以贪心策略是,每一个贵的礼物,带一个便宜的,因为带也是一组,不带也是一组,肯定选择带,且最贵的和最便宜的最容易占满一组
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N];
int main()
{
int w;cin>>w;
int n;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int ans=0;
sort(a+1,a+n+1);
int l=1;int r=n;
while(l<=r)
{
ans++;
if(l==r)
{
break;
}
if(a[l]+a[r]<=w)
{
l++,r--;
}
else
{
r--;
}
}
cout<<ans<<endl;
}
找规律的贪心,考验思维,将字符串分类讨论设计贪心策略。
先给字符串排序,然后我们可以分为三类讨论:
1.字符串全相等(假设全a),那就是尽量使得每个人分到的字符串的最大长度尽可能小就行。
2.s[x]==s[1],说明第x个是最小的字符带着后面所有的字符一起输出。
3.s[x]!=s[1],后面一坨字符可以直接丢到s[1]后面,分给第一个同学。
代码如下:
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
int main()
{
int n,x;
cin>>n>>x;
cin>>s+1;
sort(s+1,s+n+1);
if(s[1]==s[n])
{
for(int i=1;i<=n/x+(n%x?1:0);i++)
{
cout<<s[1];
}
}
else if(s[x]==s[1])
{
for(int i=x;i<=n;i++)
{
cout<<s[i];
}
}
else{
cout<<s[x];
}
return 0;
}