AT4927 [AGC033D] Complexity

https://www.luogu.com.cn/problem/AT4927

O ( n 5 ) O ( n 4 l o g n ) O(n^5)O(n^4logn) O(n5)O(n4logn)的DP显然过不了

考虑优化,修改状态,容易发现最多只会切log刀,所以我们可以把答案记在状态里

d p [ o ] [ i ] [ j ] [ k ] dp[o][i][j][k] dp[o][i][j][k]表示已经切了 o o o刀,从第 i i i行,第 j j j行,从第 k k k列开始最多能往右拓展多少

然后转移考虑竖着切就是
d p [ o ] [ i ] [ j ] [ k ] = d p [ o − 1 ] [ i ] [ j ] [    d p [ o − 1 ] [ i ] [ j ] [ k ] + 1    ] dp[o][i][j][k]=dp[o-1][i][j][ \ \ dp[o-1][i][j][k] + 1\ \ ] dp[o][i][j][k]=dp[o1][i][j][  dp[o1][i][j][k]+1  ]
横着切就是上下的取 m i n min min,显然分成上下部分尽量接近的时候是最优的,二分即可

具体实现可以看代码
code:

#include<bits/stdc++.h>
#define N 195
using namespace std;
int s[N][N], f[2][N][N][N], n, m;
int check(int x, int y, int xx, int yy) {
    int sz = (xx - x + 1) * (yy - y + 1);
    int gs = s[xx][yy] - s[x - 1][yy] - s[xx][y - 1] + s[x - 1][y - 1];
    return sz == gs || !gs;
}
int find(int o, int i, int j, int k) {
    int l = i - 1, r = j, ret = 0;
    while(l + 1 < r) {
        int mid = (l + r) >> 1;
        int x = f[o][i][mid][k];
        int y = f[o][mid + 1][j][k];
        ret = max(ret, min(x, y));
        if(x > y) l = mid;
        else r = mid;
    }
    return ret;
}
void dp(int o) {
    for(int i = 1; i <= n; i ++) {
        for(int j = i; j <= n; j ++) {
            for(int k = 1; k <= m; k ++) {
                int l = f[o ^ 1][i][j][k], s = 0;
                if(l != m) {
                    s = f[o ^ 1][i][j][l + 1];
                    s = max(s, find(o ^ 1, i, j, k));
                } else s = m;
                f[o][i][j][k] = s;
                //printf("  %d %d %d %d  %d\n", o, i, j, k, s);
            }
        }
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) 
        for(int j = 1; j <= m; j ++) {
            char c;
            scanf(" %c", &c);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + (c == '#');
        }

    // for(int i = 1; i <= n; i ++) {
    //     for(int j = 1; j <= m; j ++) printf("%d ", s[i][j]); printf("\n");
    // }
    for(int i = 1; i <= n; i ++) {
        for(int j = i; j <= n; j ++) {
            for(int k = 1; k <= m; k ++) {
                int l = k - 1, r = m + 1;
                while(l + 1 < r) {
                    int mid = (l + r) >> 1;
                    if(check(i, k, j, mid)) l = mid;
                    else r = mid;
                }
                f[0][i][j][k] = l;
             //   printf("%d %d %d  %d   \n", i, j, k, l);
            }
        }
    }   

    int ans = 0;
    while(f[ans & 1][1][n][1] < m) ans ++, dp(ans & 1);
    printf("%d", ans);
    return 0;
}
posted @ 2021-12-06 21:51  lahlah  阅读(65)  评论(0编辑  收藏  举报