CF1990D Grid Puzzle 题解

CF1990D Grid Puzzle

DP 好题。考虑分析两种操作的性质,不难发现操作 \(1\) 至多只能涂白 \(4\) 个方格,而操作 \(2\) 可以涂白的方格取决于这一行的方格数,几乎无上限。 因此,从涂白的方格数考虑,对于黑色方格数量 \(\ge5\) 的一行,我们直接使用操作 \(2\) 涂白。注意黑色方格数量等于 \(4\) 的一行不一定直接使用操作 \(2\),因为无法比较两种操作哪个更优。

之后,考虑到剩余每行的黑色方格数很少,不会超过 \(4\),考虑动态规划。不难发现每次使用操作 \(1\) 左下角一定在第一列或者第三列,否则可以挪动证明一定不优。设状态 \(f[i][j]\) 表示第 \(i\) 行状态为 \(j\) 且前 \(i-1\) 行全部被覆盖的最小操作次数。若 \(j=0\),表示没有任何方格被覆盖;若 \(j=1\),表示第一、二行的方格被覆盖,即以这一行第一列为左下角进行一次操作 \(1\);若 \(j=2\),表示第三、四行的方格被覆盖,即以这一行第三列为左下角进行一次操作 \(1\);若 \(j=3\),表示所有方格被覆盖。初始时全为负无穷。

显然,对于黑色方格数量 \(\ge5\) 的一行或本来就没有黑色方格的一行,\(f[i][3]=0\)

对于其他情况,我们把每个状态分别进行转移。

\[f[i][0]=\min(f[i][0],f[i-1][3]) \]

\[f[i][0]=\min(f[i][0],f[i-1][1])(a[i-1]\le2) \]

如果这一行没有任何方格被覆盖,为了满足定义前 \(i-1\) 行全部被覆盖,必须选取全部被覆盖的状态,即 \(f[i-1][3]\)。特别的,若 \(a[i-1]\le2\),状态 \(f[i-1][1]\) 也是完全覆盖。

\[f[i][1]=\min(f[i][1],\min(f[i-1][3]+1,f[i-1][2]+1)) \]

\[f[i][1]=\min(f[i][1],f[i-1][0]+1)(a[i-1]\le2) \]

以这一行第一列为左下角进行一次操作 \(1\),则也会覆盖上一行的第一、二列。因此,可以从 \(f[i-1][2]\) 的状态转移过来,这样上一行所有列都被覆盖。当然,也可以从 \(f[i-1][3]\) 转移。特别的,若 \(a[i-1]\le2\),从状态 \(f[i-1][0]\) 转移也是完全覆盖。

\[f[i][2]=\min(f[i][2],\min(f[i-1][3]+1,f[i-1][1]+1)) \]

同上,不过不需要考虑 \(a[i-1]\le2\) 的情况,因为没有影响。

\[f[i][3]=\min(f[i][3],\min(f[i-1][3]+2,\min(f[i-1][2]+2,\min(f[i-1][1]+2,f[i-1][0]+2)))) \]

\[f[i][3]=\min(f[i][3],f[i-1][3]+1) \]

\[f[i][3]=\min(f[i][3],f[i-1][1]+1)(a[i-1]\le2) \]

如果这一行所有方格被覆盖,那么上一行的状态可以任意选择。另外,也可以直接使用操作 \(2\),同 \(f[i][0]\) 的转移,此时应该选择 \(f[i-1][3]\) 进行转移。特别的,若 \(a[i-1]\le2\),状态 \(f[i-1][1]\) 也是完全覆盖。

最后,对于每段被 \(0\) 隔开的部分分别计算答案进行累加,并计算上最初操作 \(2\) 的贡献,即可解决问题。时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;
long long t,n,a[300000],f[300000][4];
int main()
{
	scanf("%lld",&t);
	while(t--)
	   {
	   	long long ans=0;
	   	scanf("%lld",&n);
	   	for(int i=1;i<=n;i++)
	   	    {
		    scanf("%lld",&a[i]);
		    if(a[i]>=5)ans++,a[i]=0;
		    }
		for(int i=0;i<=n;i++)
		    for(int j=0;j<=3;j++)
		        f[i][j]=1e16;
		f[0][3]=0,a[n+1]=0;
		for(int i=1;i<=n;i++)
		    if(!a[i])f[i][3]=0;
		for(int i=1;i<=n;i++)
		    if(a[i])
		       {
		       	f[i][0]=min(f[i][0],f[i-1][3]);
		       	if(a[i-1]<=2)f[i][0]=min(f[i][0],f[i-1][1]);
		       	f[i][1]=min(f[i][1],min(f[i-1][3]+1,f[i-1][2]+1));
		       	if(a[i-1]<=2)f[i][1]=min(f[i][1],f[i-1][0]+1);
		       	f[i][2]=min(f[i][2],min(f[i-1][3]+1,f[i-1][1]+1));
		       	f[i][3]=min(f[i][3],min(f[i-1][3]+2,min(f[i-1][2]+2,min(f[i-1][1]+2,f[i-1][0]+2))));
			    f[i][3]=min(f[i][3],f[i-1][3]+1);
			    if(a[i-1]<=2)f[i][3]=min(f[i][3],f[i-1][1]+1);
			   }
		for(int i=1;i<=n;i++)
		    if(a[i]!=0&&a[i+1]==0)
		       {
			   long long k=min(f[i][3],min(f[i][2]+1,min(f[i][1]+1,f[i][0]+2)));
			   if(a[i]<=2)k=min(k,f[i][1]);
			   ans+=k;
			   }
		printf("%lld\n",ans);
	   }
	return 0;
}
posted @ 2025-02-17 16:07  w9095  阅读(45)  评论(0)    收藏  举报