bzoj 1296 & 洛谷4158 [SCOI2009]粉刷匠 题解

题意简述

一个 n × m n\times m n×m的矩阵,每个位置珂能是粉色(0表示)或者是蓝色(1表示),然后你珂以对同一行里连续一段长度的区间染上一种颜色(覆盖型),你能染 t t t次,每次不限长度。求你染到的正确的颜色的个数最多是多少。

思路框架

f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i行染 j j j次最大染色个数。 g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k]表示第 i i i行染 j j j次只考虑前 k k k个格子最大方案。

具体思路

D P DP DP题分两步,设状态,求转移。

多数题目是状态不好设的。像这个题就是,我想了好久没想出来状态怎么设,看一下题解知道状态怎么设之后就是秒切。那么,我们这个题也是分两块来讲。

Part 1. 状态

首先 f f f是很好想的。答案就是 f [ n ] [ i ] ( i ∈ [ 1 , t ] ) f[n][i] (i\in [1,t]) f[n][i](i[1,t])中的最大值。

然后我们来求 f [ i ] [ j ] f[i][j] f[i][j]的转移。求着求着发现,我们先要枚举一个 k k k k k k小于 j j j。转移的一部分就是 f [ n − 1 ] [ j − k ] f[n-1][j-k] f[n1][jk],就是上一行染 j − k j-k jk次。还有 k k k次留给这一行。所以我们要加上一个数,这个数就是:第 i i i行中染 k k k次正确染色的最大值。

然后我们来求这个子问题。

Part 1.1 子问题的状态

g [ i ] [ j ] g[i][j] g[i][j]表示第 i i i行染 j j j次的答案。考虑转移。。。转你妈,缺状态。观察数据也能轻易发现,应该是 O ( n m t ) O(nmt) O(nmt)的算法,然后 O ( n t ) O(nt) O(nt)过是什么鬼,肯定还缺一维。

缺什么状态呢。我们发现,还和染到了那里有关。不妨再加上一个 k k k,表示我们考虑到第 k k k个位置。

Part 1.2 子问题的转移

然后就来推转移了。像这种有“考虑到第xxx个位置”为一个状态的 D P DP DP,通常我们用枚举断点作转移。枚举断点 q q q(我真的没名字了, i , j , k i,j,k i,j,k都用过了,就叫 q q q了,随便一点)。由定义, q q q小于 k k k。还有一个注意点, q q q是能取 0 0 0的。其原因是,我习惯枚举的是 q q q把区间分成 [ 0 , q ] [0,q] [0,q] [ q + 1 , k ] [q+1,k] [q+1,k]。显然, q q q 0 0 0时也有意义。

然后我们要看 [ q + 1 , k ] [q+1,k] [q+1,k]中是蓝色多还是粉色多,哪个多我们就染那种颜色。这样显然是对的。然后我们只要维护一个前缀和出来,维护每一行内蓝色的前缀和,记为 s u m sum sum。看是 s u m [ i ] [ k ] − s u m [ i ] [ q ] sum[i][k]-sum[i][q] sum[i][k]sum[i][q]大,还是 ( k − q ) − ( s u m [ i ] [ k ] − s u m [ i ] [ q ] ) (k-q)-(sum[i][k]-sum[i][q]) (kq)(sum[i][k]sum[i][q])大。设这两个的最大值为 S S S

那么 g [ i ] [ j ] [ k ] = m a x ( g [ i ] [ j − 1 ] [ q ] + S ) g[i][j][k]=max(g[i][j-1][q]+S) g[i][j][k]=max(g[i][j1][q]+S)

Part 1.3 时间复杂度?

看起来状态数是 O ( n m t ) O(nmt) O(nmt),转移数是 O ( m ) O(m) O(m),复杂度 O ( n m 2 t ) O(nm^2t) O(nm2t)。但是, g g g的第二维,即染了 j j j次那一维中,显然 j j j不会超过 m m m。因为这一行最多就 m m m个数,染 m m m次啥事都解决了。所以复杂度应该是 O ( n m 3 ) O(nm^3) O(nm3),即 O ( 5 0 4 ) O(50^4) O(504)。稳过。

子问题完毕

Part 2. 转移

好了,回到主线。有了 g g g之后,就珂以得到 f f f的转移:

$f[i][j]=max(f[i-1][j-k]+g[i][k][m]) $(哎哟这个式子不要太显然,我真的不想解释了)

P a r t 2 Part2 Part2 P a r t 1 Part1 Part1短很多,说明这个题重点在设状态,是个毒瘤题)

代码 (精髓)

#include 
using namespace std;
namespace Flandre_Scarlet
{
    #define N 61
    #define T 2602
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)

    int n,m,t;
    int sum[N][N];char mp[N][N];
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n),R1(m),R1(t);
        F(i,1,n)
        {
            scanf("%s",mp[i]+1);
            sum[i][0]=0;
            F(j,1,m) 
            {
                if (mp[i][j]=='1') sum[i][j]=sum[i][j-1]+1; 
                else sum[i][j]=sum[i][j-1];
            } 
        }
    }

    int f[N][T],g[N][T][N];
    void Soviet()
    {
        F(i,1,n) F(j,1,t) F(k,1,m) F(q,0,k-1)
        {
            int ss=sum[i][k]-sum[i][q];
            int S=max(ss,(k-q)-ss);
            g[i][j][k]=max(g[i][j][k],g[i][j-1][q]+S);
        }

        F(i,1,n) F(j,1,t) F(k,0,min(j,m)) f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k][m]);
        printf("%d\n",*max_element(f[n]+1,f[n]+t+1));
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}

你可能感兴趣的:(洛谷,bzoj)