【SJTUOJ笔记】P1074 LSZ的雪地脚印

https://acm.sjtu.edu.cn/OnlineJudge/problem/1074
雪地只有脚印和空白两种状态,可以分别用1和0代表。而查询某一块区域是否可行,就是查询这块区域内所有点的和是否为0,自然用二维前缀和比较合适。
设某一块矩形左上角的坐标为 (i,j) ( i , j ) ,短边长为 k k ,对 i,j,k i , j , k 进行枚举,即可得到暴力做法,时间复杂度 O(n3) O ( n 3 )

   //s是前缀和数组
   int ans = 0;
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= n; ++j){
            int maxlen = min(n - i + 1, (m - j + 1) / 2); // 当前位置的最大可能边长
            for (int l = 1; l <= maxlen; ++l){
                int x = i + l - 1, y = j + 2 * l - 1; // 右下角坐标
                if (s[x][y] - s[i - 1][y] - s[x][j - 1] + s[i - 1][j - 1] == 0){
                    ans = max(ans, (x - i + 1) * (y - j + 1));
                }
            }
        }
    }

但是,这样只能通过 40% 40 % 的数据。
再考虑一下,能否缩小枚举的边长范围。可以发现,若用 f[i,j] f [ i , j ] 表示右下角在 (i,j) ( i , j ) 的最大可行区域的边长, f[i,j] f [ i , j ] 不会超过 f[i1,j]+1 f [ i − 1 , j ] + 1
【SJTUOJ笔记】P1074 LSZ的雪地脚印_第1张图片
如图,红色部分为右下角在点A的最大可行区域,现在考虑右下角在点B的最大可行区域。若其边长比A大2(黄色部分),则黑框范围同样是右下角在点A的可行区域,矛盾。因此,区域B的边长最多比A大1(橙色部分)。利用这个性质,当整张图中脚印分布较为均匀时,可以大大缩小边长的枚举范围。

//a是原数组,s是前缀和,f如上所述
for (int i = 1; i <= n; ++i){
        for (int j = 2; j <= m; ++j){
            if (a[i][j])
                continue; //此处本身有脚印,不存在可行区域
            for (int t = f[i - 1][j] + 1; t >= 1; --t){ //枚举边长
                int x = i - t + 1, y = j - 2 * t + 1; //左上角坐标
                if (x <= 0 || y <= 0)
                    continue;
                int p = s[i][j] - s[x - 1][j] - s[i][y - 1] + s[x - 1][y - 1];
                if (p == 0){
                    f[i][j] = t;
                    break; //已经找到了一个解,就无须再考虑边长更小的解了
                }
            }
        }
    }

你可能感兴趣的:(算法与数据结构)