「JOI 2018 Final」团子制作

「JOI 2018 Final」团子制作

题面:

你是一个制作团子的师傅,现在,你正想用竹签把团子串成一串。

团子被放置在长为 \(n\) 行,宽为 \(m\) 列的隔开的格子里,每个格子里都放着一个团子。每个团子的颜色是红、绿与白中的一种。

你可以选择三个从左到右,或者从上到下的连续的格子,把格子中的团子串成一串,按照这个顺序,一串团子串上正好会有三个团子。

现在,你希望尽可能多做些颜色按照红绿白顺序的团子串,并且团子在串上的顺序必须与从格子中取出的顺序相同。需要注意的是,同一个团子只能被串在一串团子串上。

你最多能制作多少串团子串呢?

给出放置在每个格子上的团子的颜色,你需要计算最多能制作的团子串的数量。团子串的颜色必须按照红、绿、白的顺序。

\(n,m\le 3000\)


注意到,当两个团子串相交时,两个团子串中的 \(G\) 部分的哈夫曼距离一定小于等于 \(1\)。且他们在一条对角线上。那么我们可以对对角线进行 dp。

\(f_{i,0/1/2}\) 表示当前对角线上前 \(i\) 行,第 \(i\) 行不放 / 横放 / 竖放 所能得到团子串的最多个数。

那么将每条对角线上的 \(f\) 最大值累计就是答案了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e3+5;
int f[MAXN][3],A[MAXN][MAXN],n,m;
char S[MAXN][MAXN];
int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%s",S[i]+1);
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
	{
		if(S[i][j]=='R') A[i][j]=1;
		else if(S[i][j]=='G') A[i][j]=2;
		else A[i][j]=3;
	}
	int Ans=0;
	for(int sum=2;sum<=n+m;++sum)
	{
		memset(f,0,sizeof f);
		int MX=0;
		for(int i=max(1,sum-m);i<=min(n,sum-1);++i)
		{
			int j=sum-i;
			if(A[i][j]==2)
			{
				if(A[i-1][j]==1&&A[i+1][j]==3) f[i][1]=max(f[i-1][1]+1,f[i-1][0]+1);
				if(A[i][j-1]==1&&A[i][j+1]==3) f[i][2]=max(f[i-1][2]+1,f[i-1][0]+1);
			}
			f[i][0]=max(f[i-1][0],max(f[i-1][1],f[i-1][2]));
			MX=max(MX,max(f[i][0],max(f[i][1],f[i][2])));
		}
		Ans+=MX;
	}
	printf("%d\n",Ans);
}
posted @ 2021-11-16 16:47  夜空之星  阅读(149)  评论(0编辑  收藏  举报