• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【等价转换】codeforces1849 D. Array Painting

题目链接

https://codeforces.com/problemset/problem/1849/D


题意

输入 \(n(1 \leq n \leq 2e5)\) 和长为 \(n\) 的数组 \(a(0 \leq a[i] \leq 2)\)。

最初,数组的每个元素都是蓝色的。
有两种类型的操作:

  • 支付一枚硬币,选择一个蓝色元素,将其涂成红色。
  • 选择一个不等于 \(0\) 的红色元素和与其相邻的蓝色元素,将红色元素的数值减少 \(1\),然后将蓝色元素涂成红色。

把每个元素都涂成红色,最少要支付多少金币?

题解

先假设初始化\(a[i]\),此时我们需要支付一个金币,随后可以将其变为红色。
若\(a[i] == 0\),明显无法进行更多操作
若\(a[i] == 1\),可以选择一个相邻的蓝色元素染为红色,并且自身数字减少 \(1\)
若\(a[i] == 2\),可以选择两个相邻的蓝色元素染为红色,并且自身数字减少 \(2\)

其中最特殊的自然是0,因为\(0\)要么被一个相邻的非 \(0\) 红色元素染红,要么只能直接花费一个金币染红
贪心的思路自然是尽量使用相邻的非 \(0\) 染红,因为可以尽量少使用金币
那么思路自然是先找出一个连续非零子数组,然后再去染相邻的蓝色 \(0\)
但是先找出一个非 \(0\) 串,再去染相邻的 \(0\),显然是有点麻烦的
不妨做出如下等价转换:
将数组从左往右观察,既然一个非\(0\)连续子数组可以染红一个\(0\),那么计算的数量 其实等于那些\(0\)的个数
既然如此,干脆直接从左往右计算\(0\)的个数,然后当遇到第一个连续非\(0\)子数组的时候,那个连续非\(0\)子数组都可以免费染红
如果那一串非\(0\)子数组里面全\(1\),那说明他只染了他前面那一大串\(0\)的最后一个\(0\)的位置,后面他连着的数字就不能再染,例如 \(0001110\),显然最后那个\(0\) 我们是染不到了。但是如果那个串里面有个\(2\),那么就连最后的那个\(0\)也能染了,例如 \(00021110\)。
然后什么时候一串全\(1\)子数组也能染后面的\(0\)呢?就是他前面没有任何未被染色的\(0\)的时候。举个例子,比如\(1110\),代价为\(1\),比如\(0201110\),代价为\(2\)。

参考代码

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

int n, m;
int a[200007];

int main() {
	IOS
	cin >> n;
	for (int i{0}; i < n; ++ i) {
		cin >> a[i];
	}
	for (int i{0}, j{0}; i < n; ) {
		if (a[i] == 0) {
			j = 1;
			++ m;
			++ i;
		} else {
			if (!j) {
				++ m;
			} 
			bool flag = false;
			while (i < n && a[i] > 0) {
				if (a[i] == 2) flag = true;
				++ i;
			}
			if (flag || j == 0) ++ i; 
			j = 0;
		}
	}
	cout << m << '\n';
	return 0;
}

posted on 2024-07-07 22:51  RomanLin  阅读(30)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3