AtCoder Beginner Contest 203

本文详细介绍了三个与矩阵和数组操作相关的编程题目,包括寻找子矩阵第k大元素、棋盘行走路径计数以及数组优化删除策略。通过二分查找、前缀和计算以及动态规划等算法技巧,对每个问题进行了解析和解答,旨在提升对数组和矩阵操作的理解和应用能力。
摘要由CSDN通过智能技术生成

AtCoder Beginner Contest 203

D - Pond

题目链接

题意:

给你一个 n ∗ n n * n nn的矩阵,每个位置都有一个元素 A i j A_{ij} Aij ,现在给你一个整数 k k k, 让你求边长为 k k k的所有子矩阵中第 k ∗ k 2 + 1 \frac{k * k} {2} + 1 2kk+1的元素最小。

题解:

求这种第 k k k大元素,很容易想到二分答案。 如何check呢?

如果在一个矩阵中比当前check的答案小于等于的个数,大于 k k k, 那么就向小的方向分,否则向大的分。

至于怎么判断有多少个元素比它小,可以用二维前缀和判断。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 807;
int a[N][N], n, k;
int mp[N][N];

bool judge(int x) {
    int base = k * k / 2;
    base = k * k - base;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (a[i][j] <= x) {
                mp[i][j] = 1;
            } else {
                mp[i][j] = 0;
            }
            mp[i][j] = mp[i - 1][j] + mp[i][j - 1] - mp[i - 1][j - 1] + mp[i][j];
            if (i - k + 1 > 0 && j - k + 1 > 0) {
                int sum = mp[i][j] - mp[i - k][j] - mp[i][j - k] + mp[i - k][j - k];
                
                if (sum >= base) {
                    return true;
                }
            } 
        }
    }
    return false;
}

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> a[i][j];
        }
    }
    int l = 0, r = 1e9, ans = 0;
    while (l <= r) {
        int m = (l + r) / 2;
        if (judge(m)) {
            ans = m;
            r = m - 1;
        } else {
            l = m + 1;
        }
    }
    cout << ans << endl;
    return 0;
}

E - White Pawn

题目链接

题意:

给你一个 ( 2 n + 1 ) ∗ ( 2 n + 1 ) (2n + 1) * (2n + 1) (2n+1)(2n+1)的矩阵, 刚开始你的位置在 ( 0 , n ) (0, n) (0,n),然后有 m m m个位置放有黑色棋子,你只能向下移动(当且仅当下方没有黑棋),你也可向左下右下移动(当且仅当是黑棋),最后问能到达最后一行位置的个数。

题解:

模拟用set去重。

代码:

#include<bits/stdc++.h>
using namespace std;

map<int, vector<int> > mp;

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int x, y; cin >> x >> y;
        mp[x].push_back(y);
    }
    set<int> ans, temp;
    ans.insert(n);
    for (auto it: mp) {
        temp.clear();
        for (int y: it.second) {
            if (ans.count(y + 1) || ans.count(y - 1)) {
                temp.insert(y);
            }
        }
        for (int y: it.second) {
            if (ans.count(y)) {
                ans.erase(y);
            }
        }
        for (int i: temp) {
            ans.insert(i);
        }
    }
    cout << ans.size() << endl;
    return 0;
}

F - Weed

题目链接

题意:

给你一个长度为 n n n的数组 a a a,你可以删掉至少 k k k个数,然后剩余的数,你可选择一个最大值 m a x max max,把大于 ⌊ m a x 2 ⌋ \lfloor \frac{max}{2}\rfloor 2max

删掉,问最少操作多少次可以将数组 a a a全部删掉,且选择删掉的元素也要最少。

题解:

对于第二种操作,每次将大于最最大值除2都删掉,所以总共也就操作 l o g ( n ) log(n) log(n)次。我们可以枚举第二种操作的次数,然后求出第一种操作的最小次数。如何求?可以通过 d p dp dp来求,设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 1 1 1 i i i中元素全部被删掉还剩余 j j j次第二种操作已经用的第一种操作的最小值。

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 7;

int n, k, a[N], l[N];

void gao(int pos) {
    int p = upper_bound(a + 1, a + pos + 1, a[pos] / 2) - a;
    p--;
    l[pos] = p;
}

int dp[N][32];

int dfs(int p, int sum) {
    if (p < 1) {
        if (sum == 0) return 0;
        return 1e7;
    }
    if (dp[p][sum] != -1) return dp[p][sum];
    int ans = INT_MAX;
    ans = min(ans, dfs(p - 1, sum) + 1);
    ans = min(ans, dfs(l[p], sum - 1));
    return dp[p][sum] = ans;
}

int main() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++) {
        gao(i);
    }

    memset(dp, -1, sizeof(dp));
    for (int i = 0; i <= 32; i++) {
        int ans = dfs(n, i);
        if (ans <= k) {
            cout << i << " " << ans << endl;
            return 0;
        }
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值