题意
给一个网格,每个格子是黑的或者白的。定义一个网格的复杂度是:
- 这个网格只有一种颜色,则复杂度是0
- 否则,复杂度是 m i n ( m a x ( 切 一 刀 所 形 成 的 两 个 网 格 复 杂 度 ) + 1 ) min(max(切一刀所形成的两个网格复杂度)+1) min(max(切一刀所形成的两个网格复杂度)+1).
求整个网格的复杂度
n
≤
185
n\leq185
n≤185
5s
题解
- O ( n 5 ) O(n^5) O(n5)的dp是显然的
- 考虑到答案是 l o g ( h ) + l o g ( w ) log(h)+log(w) log(h)+log(w)级别的,设 f [ v ] [ i ] [ l ] [ r ] f[v][i][l][r] f[v][i][l][r]表示以 ( l , i ) (l,i) (l,i)到 ( r , i ) (r,i) (r,i)为左边界的复杂度 ≤ v \leq v ≤v的网格最右端。
- 更新方法仍然是考虑横着切还是竖着切。 O ( n 4 log n ) O(n^4 \log n) O(n4logn)
- 仍然要优化,竖着切的时候二分一下, O ( n 3 log 2 n ) O(n^3 \log^2n) O(n3log2n)
- 发现随着左边界长度的递增,竖着切的转移点是单调的。
- O ( n 3 log n ) O(n^3\log n) O(n3logn)
下面是二分的
#include <bits/stdc++.h>
using namespace std;
const int N = 190;
int n, m;
char a[N][N];
int o;
int f[2][N][N][N];
int main() {
freopen("d.in", "r", stdin);
cin >> n >> m;
for(int i = 1; i <= n; i++) scanf("%s", a[i] + 1);
for(int j = m; j; j--) {
for(int i = 1; i <= n; i++) {
for(int k = i; k <= n; k++) if(a[k][j] == a[i][j]) {
f[o][j][i][k] = j;
if (a[i][j] == a[i][j + 1]) {
f[o][j][i][k] = max(j, f[o][j + 1][i][k]);
}
} else break;
}
}
for(int ans = 0; ans <= 20; ans++) {
if (f[o][1][1][n] == m) {
printf("%d\n", ans); return 0;
}
memset(f[1 - o], 0, sizeof f[1 - o]);
for(int j = 1; j <= m; j++) {
for(int i = 1; i <= n; i++) {
for(int z = i; z <= n; z++) {
int g = f[o][j][i][z];
if (f[o][j][i][z] > 0 && f[o][j][i][z] < m)
g = max(g, f[o][ f[o][j][i][z] + 1 ][i][z]);
if (i != z) {
int l = i, r = z - 1, ret = l;
while (l <= r) {
int mid = l + r >> 1;
if (f[o][j][i][mid] >= f[o][j][mid + 1][z]) {
ret = mid, l = mid + 1;
} else r = mid - 1;
}
g = max(g, min(f[o][j][i][ret], f[o][j][ret + 1][z]));
ret ++;
if (ret < z)
g = max(g, min(f[o][j][i][ret], f[o][j][ret + 1][z]));
}
f[1 - o][j][i][z] = g;
}
int e = j;
}
}
o = 1 - o;
}
}