贪心算法(思路)

最近在cf上做了很多贪心的题,写篇博客来总结一下

Problem - C - Codeforces

看第一道题

不难看出,我们需要在数组中找到一段奇偶相间的序列,要使他们的和最大, 在图中我们假设1,2和3,4是奇偶相间的序列,我们在在这两段序列中找到一段连续的子序列使其和最大,首先我们要处理这两段序列之间的过渡,也就是我们如何从1到2跳转到3到4,这个好办,看看2到3这段序列的性质,一个奇数减去一个奇数剩下一个偶数,一个偶数减去一个偶数剩下的也是一个偶数,所以,如果是奇数与奇数或是偶数与偶数相邻,我们只需要加上(ai-ai-1)%2==0这个条件就可以把它筛选出来了,但这个条件必须配上i使用,当i=0的时候i-1数组就会越界,如果我们在(ai-ai-1)%2==0这个条件前面放一个i,就像这样i&&(ai-ai-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)+ai(ai是当前这个数组的元素)即可,这时我们会遇到一种情况,假如序列是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 = 不断取更合适的子子区间;
}

Poblem - C - Codeforcese

而这道题是为了找出每一个全局最优,可以发现当(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用于跟进最大值。

Problem - E - Codeforces

这个E其实也是一个累加问题,E我之前写过一次题解cf918div4的E题讲解-CSDN博客

大家自己看一下吧,总的来说就是从局部最优到全局最优的过程

相关推荐
灯澜忆梦5 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
小蒋观天下5 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型
Evand J5 小时前
【电机滤波例程5】渐消因子自适应扩展卡尔曼滤波(EKF),MATLAB。PMSM负载突变下的状态估计。附下载链接
算法·matlab
H.莓飛5 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
好想像大佬一样能够ak所有6 小时前
Leetcode35.搜索插入位置
数据结构·算法·leetcode
aqiu1111116 小时前
【C++算法打怪专栏】LeetCode 165. 比较版本号(双指针与字符串流法)
c++·算法·leetcode
黄金龙PLUS6 小时前
如何基于置换算法来设计对称加密算法
算法·网络安全·密码学·哈希算法·同态加密
不会就选b6 小时前
算法日常・每日刷题--<动态规划>14
算法
赛博守夜人6 小时前
跨境业务与出海合规之九:海外买量投放与营销虚假流量(Ad Fraud):跨国机房群控与归因欺诈识别算法
算法·安全
镜子AI9 小时前
教培机构AI推荐系统实战:概念漂移检测算法——为什么“去年有效的推荐今年可能失效“
人工智能·算法