通常来讲,贪心向来是取局部最优解,但是局部最优解最后不一定构成整体最优解,因而就有了反悔这个操作,来优化贪心。
而构成反悔贪心,通常会有一个限制条件。
例如,我们假设数组array{1,5,6,7,2,6,7,9,4,3,5,1};如果我们要求取k个数,使其取得最大值,显然此时我们的贪心策略是对array数组进行排序,取前k大的数据加起来就得到结果。但是当我加一个限制条件,不允许取相邻的两个数,也就是说当你取了位置i上的数时,位置i-1和位置i+1的值就不能取了,此时若按照上述贪心策略取贪心,则不能确保整体最优解。
举个最简单的例子,对于{4,5,2},如果我们取了5,则不能取4和2,但我们发现我们可以不取5,转而取4和2,而此时获得的值为4+2=6,是大于5的。这时我们发现我们第一个贪心策略是不完美了,我们需要进行反悔操作,才能达到最优解。
那么如何进行反悔操作呢。我们假设数值5对应的位置为i;则4的位置为i-1,2的位置为i+1,namo我们可以设一个大根堆存储各个元素的值及其对应的位置,首先我们采取最开始的贪心策略,我们把5给拿走,此时我们可以处理一个反悔操作,令a[i] = a[i+1] +a[i-1] -a[i] ;并将这个更新的值加入大根堆中,若我们可以取到这个值,显然更新后的a[i]含有的a[i] 项就会和我们之前贪心选择的a[i] 项消掉,留下a[i+1] 和a[i-1],这便完成了一次反悔贪心,以此类推,从小及大,最终即可得到最优解。
在这里我推荐两道例题去具体熟练一下操作:
一、洛谷的种树.
AC代码:
/*
* @Author: csc
* @Date: 2021-04-05 15:29:23
* @LastEditTime: 2021-04-06 19:32:17
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \code_formal\cf\CF_712_4_.cpp
*/
#include <bits/stdc++.h>
#define pr printf
#define sc scanf
#define sf(n) scanf("%d", &n)
#define sff(n1, n2) scanf("%d %d", &n1, &n2)
#define sfff(n1, n2, n3) scanf("%d %d %d", &n1, &n2, &n3)
#define sl(n) scanf("%lld", &n)
#define sll(n1, n2) scanf("%lld %lld", &n1, &n2)
#define slll(n1, n2, n3) scanf("%lld %lld %lld", &n1, &n2, &n3)
#define for0(i, n) for (i = 0; i < n; i++)
#define for1n(i, n) for (i = 1; i <= n; i++)
#define foran(i, a, n) for (i = a; i <= n; i++)
#define forna(i, a, n) for (i = n; i >= a; i--)
#define pb push_back
#define fi first
#define se second
#define int long long
#define endl '\n'
#define mem(ara, n) memset(ara, n, sizeof(ara))
#define memb(ara) memset(ara, false, sizeof(ara))
#define all(x) (x).begin(), (x).end()
#define sq(x) ((x) * (x))
#define sz(x) x.size()
const int N = 2e5 + 100;
const int mod = 1e9 + 7;
namespace fastIO
{
inline void input(int &res)
{
char c = getchar();
res = 0;
int f = 1;
while (!isdigit(c))
{
f ^= c == '-';
c = getchar();
}
while (isdigit(c))
{
res = (res << 3) + (res << 1)