zoukankan      html  css  js  c++  java
  • 【2018.9.20】JOI 2017 Final T3「JOIOI 王国 / The Kingdom of JOIOI」

    题目链接

    题目描述

    JOIOI 王国是一个 $H$ 行 $W$ 列的长方形网格,每个 $1 imes 1$ 的子网格都是一个正方形的小区块。为了提高管理效率,我们决定把整个国家划分成两个省 $JOI$ 和 $IOI$ 。

    我们定义,两个同省的区块互相连接,意为从一个区块出发,不用穿过任何一个不同省的区块,就可以移动到另一个区块。有公共边的区块间可以任意移动。
    我们不希望划分得过于复杂,因此划分方案需满足以下条件:

    • 区块不能被分割为两半,一半属 $JOI$ 省,一半属 $IOI$ 省。
    • 每个省必须包含至少一个区块,每个区块也必须属于且只属于其中一个省。
    • 同省的任意两个小区块互相连接。
    • 对于每一行/列,如果我们将这一行/列单独取出,这一行/列里同省的任意两个区块互相连接。这一行/列内的所有区块可以全部属于一个省。

    现给出所有区块的海拔,第 $i$ 行第 $j$ 列的区块的海拔为 $A_{i,j}$​​。设 JOI 省内各区块海拔的极差(最大值减去最小值) 为 $R_{JOI}$​​,IOI 省内各区块海拔的极差为 $R_{IOI}$​​。在划分后,省内的交流有望更加活跃。但如果两个区块的海拔差太大,两地间的交通会很不方便。 因此,理想的划分方案是 $max(R_{JOI}, R_{IOI})$ 尽可能小。
    你的任务是求出 $max(R_{JOI}, R_{IOI})$ 至少为多大。

    输入格式

    第一行,两个整数 $H,W$,用空格分隔。
    在接下来的 HHH 行中,第 $i$ 行有 $W$ 个整数 $A_{i,1}, A_{i, 2}, ldots, A_{i, W}$​​,用空格分隔。
    输入的所有数的含义见题目描述。

    输出格式

    一行,一个整数,表示 $max(R_{JOI}, R_{IOI})$ 可能的最小值。

    样例

    样例输入 1

    4 4
    1 12 6 11
    11 10 2 14
    10 1 9 20
    4 17 19 10

    样例输出 1

    11

    样例解释 1

    在这组样例中,一种理想方案长这样。下图中,$J$ 表示该区块属于 $JOI$ 省,$I$ 表示该区块属于 $IOI$ 省。

    $J$ $J$ $J$ $I$
    $J$ $J$ $J$ $I$
    $J$ $J$ $I$ $I$
    $J$ $I$ $I$ $I$

    注意下述方案不符合第四条原则,将第三列单独取出时,两个 $I$ 不能互相连接。

    $J$ $J$ $I$ $I$
    $J$ $J$ $J$ $I$
    $J$ $J$ $I$ $I$
    $J$ $I$ $I$ $I$

     

     

     

    样例输入 2

    8 6
    23 23 10 11 16 21
    15 26 19 28 19 20
    25 26 28 16 15 11
    11 8 19 11 15 24
    14 19 15 14 24 11
    10 8 11 7 6 14
    23 5 19 23 17 17
    18 11 21 14 20 16

    样例输出 2

    18

    数据范围与提示

    对于 $15\%$ 的数据,$H, Wleqslant 10$。
    对于另外 $45\%$ 的数据,$H, Wleqslant 200$。
    对于所有数据,$2leqslant H, Wleqslant 2000, A_{i,j}leqslant 10^9(1leqslant ileqslant H, 1leqslant jleqslant W)$。

     

    上网搜这JOI的题,一篇题解都没找到,好尬啊,只好回日本 JOI2017 原网站看了半天日语题解日语水平菜头皮发麻

    看看$N,M$,乘起来共有400w个数,再看看答案的值域,$[0, 10^9(max-min)]$。

    好像可以二分答案并$n^2$判断?

    于是我们只需要再知道怎么判断分割是否可行。

    其实,题目说的比较含蓄,整个地图划分的两个省其实都类似于旋转三角:

    原因是这两条:

    • 同省的任意两个小区块互相连接。
    • 对于每一行/列,如果我们将这一行/列单独取出,这一行/列里同省的任意两个区块互相连接。这一行/列内的所有区块可以全部属于一个省

    所以判断就比较方便了。

    我们可以先找出海拔的最小值$AllMin$和最大值$AllMax$。

    然后二分答案与海拔最小值的差(这样更好,我直接二分答案然后莫名被卡)$x$。

    我们就要判断现在能否划分出两个类似于上图的三角,如果能就缩小答案,如果不能就增大答案。

    那么让一块的最大值$leq AllMin+x$,一块的最小值$geq AllMax-x$,依次判断上述四种情况是否有一种满足即可。

    比如让红色省区的最大值$leq AllMin+x$,以第二张图为例,只需要从第一行开始在每行中遍历。如果遍历到 第一个海拔$geq$最大值的地方 或者 超过上一行划分的红色省区右端的地方 就停(这一格不划入红色省区),转到下一行即可。否则将该格划入红色省区。

    然后每行右边没划的地方自然全给蓝色省区,再判一下蓝色省区的最小值是否$geq AllMax-x$就行了。

    其它情况类似。

    时间复杂度$O(H*W*log(AllMax-AllMin))$。

     1 #include <cstdio>
     2 #include <algorithm>
     3 using namespace std;
     4 
     5 int H, W, A[2020][2020];
     6 int gmin, gmax;
     7 
     8 bool check(int th)
     9 {
    10     int sep = 0;
    11     for (int i = 0; i < H; ++i) {
    12         for (int j = 0; j < W; ++j) {
    13             if (A[i][j] < gmax - th) {
    14                 sep = max(sep, j + 1);
    15             }
    16         }
    17         for (int j = 0; j < W; ++j) {
    18             if (gmin + th < A[i][j]) {
    19                 if (j < sep) return false;
    20             }
    21         }
    22     }
    23     return true;
    24 }
    25 void flip_row()
    26 {
    27     for (int i = 0; i < H / 2; ++i) {
    28         for (int j = 0; j < W; ++j) {
    29             swap(A[i][j], A[H - 1 - i][j]);
    30         }
    31     }
    32 }
    33 void flip_col()
    34 {
    35     for (int i = 0; i < H; ++i) {
    36         for (int j = 0; j < W / 2; ++j) {
    37             swap(A[i][j], A[i][W - 1 - j]);
    38         }
    39     }
    40 }
    41 int solve()
    42 {
    43     int lo = 0, hi = gmax - gmin;
    44     while (lo < hi) {
    45         int mid = (lo + hi) / 2;
    46         if (check(mid)) {
    47             hi = mid;
    48         } else {
    49             lo = mid + 1;
    50         }
    51     }
    52     return lo;
    53 }
    54 int main()
    55 {
    56     scanf("%d%d", &H, &W);
    57     for (int i = 0; i < H; ++i) {
    58         for (int j = 0; j < W; ++j) {
    59             scanf("%d", &(A[i][j]));
    60         }
    61     }
    62 
    63     gmin = gmax = A[0][0];
    64     for (int i = 0; i < H; ++i) {
    65         for (int j = 0; j < W; ++j) {
    66             gmin = min(gmin, A[i][j]);
    67             gmax = max(gmax, A[i][j]);
    68         }
    69     }
    70 
    71         //翻转三次,得到四种情况
    72     int ret = solve();
    73     flip_row();
    74     ret = min(ret, solve());
    75     flip_col();
    76     ret = min(ret, solve());
    77     flip_row();
    78     ret = min(ret, solve());
    79     printf("%d
    ", ret);
    80     return 0;
    81 }
    View Code
  • 相关阅读:
    Codeforces Round #183 (Div. 2) B. Calendar
    FZU Problem 2030 括号问题
    NEU(1262: ASCII Sequence II)动态规划
    ZOJ(1711)Sum It Up (DFS+剪枝+去重复)
    ZOJ(1004)Anagrams by Stack (DFS+stack)
    HDU(3374) (KMP + 最小表示法)
    FZU Problem 1926 填空(KMP好题一枚,确实好)
    POJ(2481)Cows 树状数组
    HOJ (1042) 整数划分
    LeetCode: Two Sum
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/9687495.html
Copyright © 2011-2022 走看看