[SCOI2009]粉刷匠

题目

  点这里看题目。

分析

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

[f(i,j)=max_{1le kle 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_{0le 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( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/12827923.html