题面
题意
给出一个矩阵,若一个矩阵中的所有元素都相同,则这个矩阵的代价为0,反之可以将它分成两个子矩阵,代价为两个子矩阵的代价的较大值+1.
做法
因为答案是log级别的,所以可以考虑枚举答案,记
d
p
[
l
]
[
r
]
[
i
]
dp[l][r][i]
dp[l][r][i]从第l行到第r行从第i列开始最多可以向右扩展到哪一列所形成的矩阵的代价为当前答案,若
d
p
[
1
]
[
n
]
[
1
]
=
m
dp[1][n][1]=m
dp[1][n][1]=m,则表示当前答案即为整个矩形的代价.
a
n
s
=
0
ans=0
ans=0时的答案很好处理,考虑如何将此时的答案转移到ans+1时的答案.一个矩形有两种拼接方法:
1.两个所在行相同的相邻矩形,直接转移:
d
p
[
l
]
[
r
]
[
i
]
=
d
p
[
l
]
[
r
]
[
d
p
[
l
]
[
r
]
[
i
]
]
dp[l][r][i]=dp[l][r][dp[l][r][i]]
dp[l][r][i]=dp[l][r][dp[l][r][i]]
2.两个所在列相同的相邻矩形,
d
p
[
l
]
[
r
]
[
i
]
=
max
x
=
l
r
−
1
m
i
n
(
d
p
[
l
]
[
x
]
[
i
]
,
d
p
[
x
+
1
]
[
r
]
[
i
]
)
dp[l][r][i]=\max_{x=l}^{r-1}min(dp[l][x][i],dp[x+1][r][i])
dp[l][r][i]=maxx=lr−1min(dp[l][x][i],dp[x+1][r][i]),这个可以通过二分找到最优解.
代码
#include<bits/stdc++.h>
#define N 200
using namespace std;
int m,n,ans,dp[2][N][N][N];
char mm[N][N];
bool now,cur;
inline void Max(int &u,int v){if(v>u) u=v;}
int main()
{
int i,j,p,q,l,r,mid;
cin>>m>>n;
for(i=1;i<=m;i++) scanf("%s",mm[i]+1);
now=1;
for(q=1;q<=m;q++)
{
for(p=q;p>=1;p--)
{
for(i=n;i>=1;i--)
{
if(p==q)
{
if(i==n) dp[now][p][q][i]=n+1;
if(mm[p][i]==mm[p][i+1]) dp[now][p][q][i]=dp[now][p][q][i+1];
else dp[now][p][q][i]=i+1;
}
else
{
if(mm[q-1][i]==mm[q][i]) dp[now][p][q][i]=min(dp[now][p][q-1][i],dp[now][q][q][i]);
else dp[now][p][q][i]=i;
}
}
}
}
for(ans=0;;ans++)
{
if(dp[now][1][m][1]==n+1)
{
cout<<ans;
return 0;
}
swap(now,cur);
for(p=1;p<=m;p++)
{
for(q=p;q<=m;q++)
{
for(i=1;i<=n;i++)
{
if(dp[cur][p][q][i]==n+1) dp[now][p][q][i]=n+1;
else dp[now][p][q][i]=dp[cur][p][q][dp[cur][p][q][i]];
for(l=p,r=q;l<r;)
{
mid=((l+r)>>1);
Max(dp[now][p][q][i],min(dp[cur][p][mid][i],dp[cur][mid+1][q][i]));
if(dp[cur][p][mid][i]>dp[cur][mid+1][q][i]) l=mid+1;
else r=mid;
}
}
}
}
}
}