题解:AT_joisc2019_h ランプ (Lamps)
题意
走廊上有排成一列的 \(n\) 盏灯,给出了一个 \(01\) 串 \(S\) 表示其开关状态(\(1\) 表示打开,\(0\) 表示关闭)。现在想要把这 \(n\) 盏灯变成目标状态 \(T\)。
你有三种操作:
- OFF 操作:选择一个区间,将区间内所有的灯关闭。
- ON 操作:选择一个区间,将区间内所有的灯打开。
- TOG 操作:选择一个区间,在区间内关闭原本打开的灯,打开原本关闭的灯。
求将灯的状态从 \(S\) 变为 \(T\) 的最小的操作数。
分析
首先,任意两个有交的翻转操作 \([l_1, r_1]\) 和 \([l_2, r_2]\)(其中 \(l_1\leq l_2\))可以化成两个不交的翻转操作 \([l_1, l_2)\) 和 \((r_1, r_2]\)。
所以在最优情况下存在翻转操作互不相交的方案。
然后考虑覆盖操作。
首先显然覆盖操作的区间要么不交,要么一个包含另一个。
正确性显然。
后一种情况可以化为先覆盖再翻转,所以存在覆盖操作互不相交的方案。
最后考虑覆盖操作和翻转操作之间的关系。
如果是先翻转再覆盖,此时翻转区间一定包含覆盖区间。
那么就可以先覆盖相反的值,然后再翻转。
所以存在覆盖操作互不相交,且翻转操作互不相交,且先覆盖再翻转的方案。
据此,可以进行线性 dp。
令 \(dp_{i, v}(v\in\{0, 1, 2\})\) 为考虑到第 \(i\) 位,其覆盖结果分别为用 \(0\) 覆盖、用 \(1\) 覆盖、不覆盖所需的最少操作数。
分情况讨论是否需要添加翻转即可。
Code
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000006
int dp[maxn][3];
char S[maxn], T[maxn];
int main()
{
int n;
scanf("%d%s%s", &n, S+1, T+1);
dp[1][0]=(T[1]!='0')+1;
dp[1][1]=(T[1]!='1')+1;
dp[1][2]=(T[1]!=S[1]);
for(int i=2;i<=n;i++)
dp[i][1]=min({dp[i-1][0]+(T[i]!='1')*(T[i-1]=='0')+1, dp[i-1][1]+(T[i]!='1')*(T[i-1]=='1'), dp[i-1][2]+(T[i]!='1')*(T[i-1]==S[i-1])+1}),
dp[i][0]=min({dp[i-1][0]+(T[i]!='0')*(T[i-1]=='0'), dp[i-1][1]+(T[i]!='0')*(T[i-1]=='1')+1, dp[i-1][2]+(T[i]!='0')*(T[i-1]==S[i-1])+1}),
dp[i][2]=min({dp[i-1][0]+(S[i]!=T[i])*(T[i-1]=='0'), dp[i-1][1]+(S[i]!=T[i])*(T[i-1]=='1'), dp[i-1][2]+(S[i]!=T[i])*(S[i-1]==T[i-1])});
printf("%d\n", min({dp[n][0], dp[n][1], dp[n][2]}));
}

浙公网安备 33010602011771号