【LOJ 3037】开关游戏(DP)

开关游戏

题目链接:LOJ 3037

题目大意

给你两个 01 串,分别是初始串和目标串,你可以有三种操作:选择一个区间,把区间里面的都变成 0/1,或者把区间里面的 01 反转。
问你最少要操作多少次把初始串变成目标串。

思路

考虑每个位置最后的结果会受什么印象。
首先是覆盖这个区间的操作,接着一个变成 0/1 的会取消前面所有的作用,那就是可能是前面有变成 0/1,再配一个可能的翻转(因为两次的话就相当于抵消了,而且没有必要)

那你可以考虑从左往右考虑,直接记录当前点的覆盖状态(根据是否有覆盖和根据是否有翻转,一共 6 种状态)。
然后考虑从这个点的 x 状态到下一个点的 y 状态,要花费多少次数。
那这个因为是区间,所以我们是能继承就继承,要停止就停止,要新增才需要增加操作次数。(类似区间拆成扫描线)
不过要注意的是要判断当前状态是否能使得这个点变成目标串里面的数字。

代码

#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 1e6 + 100; int n, a[N], b[N]; int f[N][3][2], ans; //0:没有,1:强制变成0,2:强制变成1 //0:没有,1:翻转 int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%1d", &a[i]); for (int i = 1; i <= n; i++) scanf("%1d", &b[i]); memset(f, 0x7f, sizeof(f)); ans = f[0][0][0]; f[0][0][0] = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j <= 2; j++) for (int k = 0; k <= 1; k++) { for (int jj = 0; jj <= 2; jj++) for (int kk = 0; kk <= 1; kk++) { if (jj == 0 && ((a[i] ^ b[i]) ^ kk)) continue; if (jj != 0 && ((jj - 1) ^ kk) != b[i]) continue; int now = f[i - 1][j][k]; if (jj && (jj != j)) now++; if (kk && !k) now++; f[i][jj][kk] = min(f[i][jj][kk], now); } } } for (int j = 0; j <= 2; j++) for (int k = 0; k <= 1; k++) ans = min(ans, f[n][j][k]); printf("%d", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/LOJ_3037.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(302)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示