E
如此唐氏的 DP 我居然想了這麼久我是不是廢了。
我們先思考一下什麼情況下會崩潰。
觀察到對於一個髒格子 \((i,j)\) 如果 \((3-i,j-1)\) 是髒的,那麼如果掃地機移動到了 \((i,j-1)\) ,它就會崩潰。
這指向我們去維護一下下一行上下兩個格子的狀態,即他們是否被清理。
故我們定義 \(dp_{i,0/1,0/1,0/1}\) 表示當前停留在 \((j,i)\) 上,且 \((3-j,i)\) 如果是髒的,已被清理,第 \(i+1\) 列上下兩個格子是否被清理的狀態。
你考慮直接列舉 \(i-1\) 的 \(0/1\) 狀態判斷合法直接轉移即可,細節見程式碼。
個人認為有一篇比較好的解釋
#include <bits/stdc++.h>
using namespace std;
#define maxn 200005
int n;
int a[2][maxn];
int dp[maxn][2][2][2];
bool check(int x, int y, int s0, int x0, int s1, int x1)
{
if(!x)
{
if(!y) return !x0;
else return !(x0 && s1) && x0;
}
else
{
if(!y) return !(x1 && s0) && s0;
else return !s0;
}
}
int main()
{
scanf("%d", &n);
int sum = 0;
for (int i = 0; i < 2; ++i) for (int j = 1; j <= n; ++j) scanf("%1d", &a[i][j]), sum += a[i][j];
memset(dp, 0x3f, sizeof(dp));
dp[0][0][0][0] = 0, dp[0][0][0][1] = dp[0][0][1][0] = 1, dp[0][0][1][1] = 2;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j < 2; ++j)
{
for (int k = 0; k < 2; ++k)
{
for (int l = 0; l < 2; ++l)
{
for (int J = 0; J < 2; ++J)
{
for (int K = 0; K < 2; ++K)
{
for (int L = 0; L < 2; ++L)
{
if(check(j, J, (k ? 0 : a[0][i]), (l ? 0 : a[1][i]), (K ? 0 : a[0][i + 1]), (L ? 0 : a[1][i + 1])))
dp[i][J][K][L] = min(dp[i][J][K][L], dp[i - 1][j][k][l] + K + L);
}
}
}
}
}
}
}
cout << sum - min(dp[n][0][0][0], dp[n][1][0][0]) << endl;
return 0;
}