「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);
}
路漫漫其修远兮,吾将上下而求索。