[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;
}
标签:const,int,粉刷,最多能,read,SCOI2009,MAXM,DP 来源: https://www.cnblogs.com/crashed/p/12827923.html