蓝书乱刷

P2862 [USACO06JAN] Corral the Cows G

题意简述

给定一个网格 \(L \times L\),上面有 \(N\) 个叶子,求最小的正方形边长,使得这个正方形能够覆盖至少 \(C\) 个叶子

题解

很显然是一道二分 + 前缀和的题目,一眼 \(O(L^2 + N \log L)\)

但是题目里 \(L\) 非常大,带个平方过不去,所以考虑对叶子坐标进行离散化,离散化之后的复杂度能降到 \(O(N^2 + N \log L)\)

问题是,本题里我们需要考虑原数据的大小啊,不然没法求边长

所以用离散化做的难点在于,代码中对原始数据和离散化数据的转换非常多

image

只有90分,我也调不出来了

const int MAXL = 1e5 + 10;
const int MAXN = 5000 + 10;

struct Node {
    int x, y;
    bool operator < (const Node &that) const {
        return x == that.x ? y < that.y : x < that.x;
    }
} node[MAXN]; 

int n, c;
int index[MAXL], ori[MAXL], cnt; bool exi[MAXL];
int sum[MAXN][MAXN];

void lisan() {
    for (int i_o = 1; i_o <= MAXL - 10; ++i_o) {
        if (exi[i_o]) {
            ori[++cnt] = i_o;
        } index[i_o] = cnt;
    }
}

bool check(int len) {
    // DEBUG(index[len]);
    for (int tx_n = index[len]; tx_n <= cnt; ++tx_n) {
        for (int ty_n = index[len]; ty_n <= cnt; ++ty_n) {
            // (rx - len, ry - len) 就已经是左下角的前一个了
            // 无需再次减一
            if (ori[tx_n] - len < 0 || ori[ty_n] - len < 0) continue;
            int lx_n = index[ori[tx_n] - len], ly_n = index[ori[ty_n] - len];
            int ss = sum[tx_n][ty_n] - sum[tx_n][ly_n] - sum[lx_n][ty_n] + sum[lx_n][ly_n];
            if (ss >= c) return true;
        } 
    } return false;
}

int main() {
    c = read(); n = read();
    for (int i = 1; i <= n; ++i) {
        node[i].x = read(); node[i].y = read();
        exi[node[i].x] = exi[node[i].y] = true;
    } lisan();
    for (int i = 1; i <= n; ++i) {
        node[i].x = index[node[i].x];
        node[i].y = index[node[i].y];
        ++sum[node[i].x][node[i].y];
        // DEBUG(node[i].x);
    }
    for (int i = 1; i <= cnt; ++i) {
        for (int j = 1; j <= cnt; ++j) {
            sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
        }
    }
    int l = 1, r = ori[cnt], ans = r;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid, r = mid - 1;
        } else l = mid + 1;
    } printf("%d\n", ans);
    return 0;
}

POJ 1050 To The Max

题意简述

给定一个 \(N\times N\) 的矩阵,求一个正方形使得数字和最大

题解

一行很好做,你既可以说是贪心也可以说是 DP:设 dp[i] 表示以 i 结尾的最大子串和,显然 dp[i] = max(dp[i - 1] + aa[i], aa[i])

但是这时候是个矩阵,怎么办?

我们考虑降维打击!

将多个连续的行压缩成一行,对应元素的值相加,然后对这一行做 DP,就相当于是对原矩阵中多个连续的行做 DP 了!

一共有 \(\frac{1}{2}N(N-1)\) 个这样的连续行组合,总时间复杂度 \(O(N^3)\)

const int MAXN = 100 + 10;

int n, mat[MAXN][MAXN];
int calc[MAXN];
int ans;

void DP() {
    static int dp[MAXN];
    for (int i = 1; i <= n; ++i) {
        dp[i] = std::max(dp[i - 1] + calc[i], calc[i]);
        ans = std::max(ans, dp[i]);
    } 
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) mat[i][j] = read();
    }
    for (int l = 1; l <= n; ++l) {
        for (int i = 1; i <= n; ++i) calc[i] = mat[l][i];
        DP();
        for (int t = l + 1; t <= n; ++t) {
            for (int i = 1; i <= n; ++i) calc[i] += mat[t][i];
            DP();
        }
    } printf("%d\n", ans);
}

posted @ 2023-10-03 17:06  Handwer  阅读(15)  评论(0编辑  收藏  举报