[SCOI2009]粉刷匠

题目

  点这里看题目。

分析

  首先可以考虑一个比较粗糙的大 DP :
  \(f(i,j)\):前\(i\)行,刷\(j\)次,最多能刷的正确格子数。
  转移是一个背包:

\[f(i,j)=\max_{1\le k\le m}\{f(i-1,j-k)+con(i,k)\} \]

  其中\(con(i,k)\)表示第\(i\)行刷\(k\)次最多能刷的正确格子数。
  我们发现,由于\(con\)是按行独立的,因此对于每行我们可以再做一次 DP 处理出\(con\)
  对于第\(i\)行,我们有如下的 DP :
  \(g(j,k)\):前\(j\)个格子刷\(k\)次最多能刷的正确格子数。
  设\(mx(j,k)\)为第\(i\)行刷一次区间\([j,k]\)最多能刷的正确格子数,也就是区间内数量最多的颜色的数量。
  转移显然:

\[g(j,k)=\max_{0\le l< j}\{g(l,k-1)+mx(l+1,j)\} \]

  内层 DP 一次\(O(m^3)\),做\(n\)次就是\(O(nm^3)\);外层 DP \(O(nmT)\)。总时间\(O(nm^3+nmT)\)

代码

#include <cstdio>

const int INF = 0x3f3f3f3f;
const int MAXN = 55, MAXM = 55, MAXT = 2505;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MAX( const _T a, const _T b )
{
	return a > b ? a : b;
}

int g[MAXM][MAXM], f[MAXT];
int col[MAXN][MAXM];
int N, M, T;

int main()
{
	read( N ), read( M ), read( T );
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = 1 ; j <= M ; j ++ )
			scanf( "%1d", &col[i][j] );
	for( int i = 1 ; i <= T ; i ++ ) f[i] = -INF;
	for( int i = 1 ; i <= M ; i ++ ) g[0][i] = -INF;
	for( int i = 1 ; i <= N ; i ++ )
	{
		for( int j = 1 ; j <= M ; j ++ )
			for( int k = 1 ; k <= j ; k ++ )
			{
				g[j][k] = -INF; int cnt[2] = {};
				for( int l = j - 1 ; ~ l ; l -- )
					cnt[col[i][l + 1]] ++, g[j][k] = MAX( g[j][k], g[l][k - 1] + MAX( cnt[0], cnt[1] ) );
			}
		for( int j = T ; j ; j -- )
			for( int k = 1 ; k <= M && k <= j ; k ++ )
				f[j] = MAX( f[j], f[j - k] + g[M][k] );
	}
	write( f[T] ), putchar( '\n' );
	return 0;
}
posted @ 2020-05-04 19:47  crashed  阅读(125)  评论(0编辑  收藏  举报