最近在cf上做了很多贪心的题,写篇博客来总结一下
看第一道题
不难看出,我们需要在数组中找到一段奇偶相间的序列,要使他们的和最大, 在图中我们假设[1,2]和[3,4]是奇偶相间的序列,我们在在这两段序列中找到一段连续的子序列使其和最大,首先我们要处理这两段序列之间的过渡,也就是我们如何从1到2跳转到3到4,这个好办,看看2到3这段序列的性质,一个奇数减去一个奇数剩下一个偶数,一个偶数减去一个偶数剩下的也是一个偶数,所以,如果是奇数与奇数或是偶数与偶数相邻,我们只需要加上(a[i]-a[i-1])%2==0这个条件就可以把它筛选出来了,但这个条件必须配上i使用,当i=0的时候i-1数组就会越界,如果我们在(a[i]-a[i-1])%2==0这个条件前面放一个i,就像这样i&&(a[i]-a[i-1])%2==0,当i==0的时候就会结束,不会进行下一个条件的判断。
我们再来看下一个问题,如何在[1,2]和[3,4]中找到和最大的子连续序列,这一步其实是为了证明贪心可行性,来思考一下什么时候我们要抛弃上一个子序列而选择下一个子序列,当上一个子序列和为负数的时候我们是不是要另开一个子序列,假设总序列为3 2 -9 4 5,我们发现3+2-9是负数了,如果我们选择把3 2 -9 4 5全都加起来,是不是会对4 5这个序列不利,既然是负的贡献,我们不妨把它抛弃掉只取4 5,这时候我们发现3+2-9<0的,给定一个变量suf我们直接取suf=std::max(suf,0)+a[i](a[i]是当前这个数组的元素)即可,这时我们会遇到一种情况,假如序列是17 4 -29 6 1怎么办,明显17+4要大于6+1,所以这是我们还需要一个变量ans用来记录前面的最大值就可以解决这个问题了
cpp
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int ans = -1E9;
int suf = -1E9;
for (int i = 0; i < n; i++) {
if (i && (a[i] - a[i - 1]) % 2 == 0) {
suf = 0;
}
suf = std::max(suf, 0) + a[i];
ans = std::max(ans, suf);
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
所以这种贪心适合于在一个数组里,存在很多段符合规律的子段,我们需要在子段里找到满足要求的子子段,通常是和最小或者最大和。这道题是局部最优找全局最优
可以总结出一个模版如下
cpp
int ans = -1E9;
int suf = -1E9;
for (int i = 0; i < n; i++) {
if (i && 条件) {
suf = 0;
}
suf = 用于积累;
ans = 不断取更合适的子子区间;
}
而这道题是为了找出每一个全局最优,可以发现当(ai+aj)/2*2 ai和aj一个为奇数一个为偶数的时候才会产生运算后的结果比运算前的结果小1的效果,只有偶数的时候不可能产生奇数,而只有奇数的时候却可以产生偶数,进而产生-1的效果,所以当i为奇数的时候,masha会优先合成两个奇数,i为偶数的时候olya会将一个奇数和一个偶数合成,由于玛莎是先手,所以在olya的回合必定会存在偶数,所以我们可以发现每消耗3个奇数就可以产生一个-1,所以就相当于分配m个奇数,i=1玛莎会消耗2,i=2,olya会消耗1,以此类推,也就是说当我们消耗完3个奇数,下一次被分配必定是玛莎,所以只需要用m/3就可以知道有多少个-1产生,那么m%2=2的时候,此时还剩下两个奇数,刚好分配给玛莎不会产生-1,m%3=1时,只剩下一个奇数了,玛莎不得不合成奇数和偶数产生-1。
cpp
using i64 = long long;
void solve() {
int n;
std::cin >> n;
i64 sum = 0;
int odd = 0;
for (int i = 1; i <= n; i++) {
int a;
std::cin >> a;
sum += a;
odd += a % 2;
int res;
if (odd == 1 && i == 1)res = 0;
else {
res = odd / 3;
if (odd % 3 == 1) {
res += 1;
}
}
std::cout << sum - res << " \n"[i == n];
}
}
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
所以代码如下,odd用于累加,res用于跟进最大值。
这个E其实也是一个累加问题,E我之前写过一次题解cf918div4的E题讲解-CSDN博客
大家自己看一下吧,总的来说就是从局部最优到全局最优的过程