CF EDU 136 總結

Saltyfish6發表於2024-10-10

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;
}