地址:https://atcoder.jp/contests/agc033/

D Complexity

dp[ i ][ j ][ k ][ l ] 表示左上角是 ( i , j ) 、右下角是 ( k , l ) 的矩阵的最小代价。

注意到答案是 log(n) + log(m) 级别的,因为每次从中间分, log 次之后就变成一行/列,log(n)+log(m)次就变成 1*1 的格子,代价是 0 。

所以把值和角标互换,dp[ i ][ j1 ][ j2 ][ k ] 表示左上角是 ( i , j1 ) 、右上角是 ( i , j2 ) 、用 k 的代价,往下最长能延伸到哪行。

转移的时候考虑横着切与竖着切。令 d = dp[ i ][ j1 ][ j2 ][ k ] ,首先有 d = dp [ dp[ i ][ j1 ][ j2 ][ k-1 ] ] [ j1 ][ j2 ][ k-1 ] ;

然后考虑 j3 满足 j1 <= j3 < j2 ,使得 [ j1 , j3 ] 和 [ j3+1 , j2 ] 就是分出的两部分;

如果 dp[ i ][ j1 ][ j3 ][ k-1 ] 和 dp[ i ][ j3+1 ][ j2 ][ k-1 ] 有一个是 < d 的,那么说明在这里切之后,两边有一个部分的代价 > k-1 ;

考虑二分找出最大的 min( dp[ i ][ j1 ][ j3 ][ k-1 ] , dp[ i ][ j3+1 ][ j2 ][ k-1 ] ) 。因为固定一条边之后,矩形越大,代价越大,所以根据两个值的大小关系来二分即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=190;
int n,m,sm[N][N],dp[2][N][N][N];
char ch[N][N];
bool chk(int x1,int y1,int x2,int y2)
{
  int s=sm[x2][y2]-sm[x2][y1-1]-sm[x1-1][y2]+sm[x1-1][y1-1];
  return (!s)||s==(x2-x1+1)*(y2-y1+1);
}
int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    scanf("%s",ch[i]+1);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)sm[i][j]=sm[i][j-1]+(ch[i][j]=='.');
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)sm[i][j]+=sm[i-1][j];
  for(int i=1;i<=n;i++)
    for(int j1=1;j1<=m;j1++)
      for(int j2=j1;j2<=m;j2++)
    {
      int l=i,r=n,ans=i-1;
      while(l<=r)
        {
          int mid=l+r>>1;
          if(chk(i,j1,mid,j2))ans=mid,l=mid+1;
          else r=mid-1;
        }
      dp[0][i][j1][j2]=ans;
    }
  if(dp[0][1][1][m]==n){puts("0");return 0;}
  bool u=1,v=0;
  for(int t=1;t<=16;t++,swap(u,v))
    {
      for(int i=1;i<=n;i++)
    for(int j1=1;j1<=m;j1++)
      for(int j2=j1;j2<=m;j2++)
        {
          int d=dp[v][dp[v][i][j1][j2]+1][j1][j2];
          if(dp[v][i][j1][j2]==n)
        { dp[u][i][j1][j2]=n;continue;}
          int l=j1,r=j2-1;
          while(l<=r)
        {
          int mid=l+r>>1;
          int t1=dp[v][i][j1][mid],t2=dp[v][i][mid+1][j2];
          d=Mx(d,Mn(t1,t2));
          if(t1>=t2)l=mid+1; else r=mid-1;
        }
          dp[u][i][j1][j2]=d;
        }
      if(dp[u][1][1][m]==n)
    {printf("%d\n",t);return 0;}
    }
}
View Code

相关文章: