W
e
l
c
o
m
e
: )

题解: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]}));
}
posted @ 2024-08-25 20:59  Jimmy-LEEE  阅读(21)  评论(0)    收藏  举报