AtCoder Beginner Contest 403

再来一场atCoder,这一场简直血虐,让你回忆起了审题的重要性

A - Odd Position Sum 

思路:题意很简单,求一个数组奇数位上数字和。很简单的问题,但你如果不仔细审题,就会浪费大量的时间

/*
Author Owen_Q
*/
#include <bits/stdc++.h>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    int oddSum = 0;
    for (int i = 0; i < n; i++) {
        int a;
        cin >> a;
        if (i % 2 == 0) {
            oddSum += a;
        }
    }
    cout << oddSum << '\n';

    return 0;
}

B - Four Hidden 

思路:第二题,简单的字符串子串题,暴力匹配一下即可

/*
Author Owen_Q
*/
#include <bits/stdc++.h>

using namespace std;

bool streamContains(string t, string u, int startT) {
    bool yes = true;
    int uLen = int(u.size());
    for (int i = 0; yes && i < uLen; i++) {
        if (u[i] != t[i + startT] && t[i + startT] != '?') {
            yes = false;;
        }
    }
    return yes;
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    string t, u;
    cin >> t >> u;
    int tLen = int(t.size());
    int uLen = int(u.size());
    for (int i = 0; i + uLen <= tLen; i++) {
        if (streamContains(t, u, i)) {
            cout << "Yes" << '\n';
            return 0;
        }
    }
    cout << "No" << '\n';
    return 0;
}

C - 403 Forbidden 

思路:又是熟悉的模拟,与上次的模拟登录类似,这次换成了模拟授权。没什么难度,但却因为没读清题目,要求输出Yes/No,结果输出了YES/NO,导致了三次无意义的WA,实属可惜

/*
Author Owen_Q
*/
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;

set<int> g[N];
bool all[N];

int main() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    int n, m, q;
    cin >> n >> m >> q;
    while (q--) {
        int o, x, y;
        cin >> o;
        if (o == 1) {
            cin >> x >> y;
            g[x].insert(y);
        } else if (o == 2) {
            cin >> x;
            all[x] = true;
        } else {
            cin >> x >> y;
            if (all[x] || g[x].contains(y)) {
                cout << "Yes" << endl;
            } else {
                cout << "No" << endl;
            }
        }
    }
    return 0;
}

D - Forbidden Difference 

思路:给定一个数组a(2e5的数据量,最大值为1e6)和一个特定的数d,要求删除最少得数量是的数组内所有数的距离都不为d

题意倒是很简单,首先想到的将距离为d的数通过并查集连接,再通过遍历每一个连通集,进行间隔删除,最后找出最小的删除方案。

第一次提交出现了部分re,显然,并查集记得初始化,这个也算是一个坑点了

注意特判一下d为0的场景

发现问题了间隔删除并非是最优策略,看来还是要用dp来做。

最后整理一下,其实并查集的主要就是为了优化连通集,但是这题的数组最大值只有1e6,因此可以直接用计数数组而放弃并查集优化,这样会使代码简洁一些。

整体的dp方案,当然是根据数组内的值是否去除来进行转移。由于要去除最小值,我们完全可以转换为保留的最大值进行转移,最终用总和进行相减即可。

不难发现,连通数组的数量最大即为特定间隔数 d,于是遍历每一个连通集,将每个连通数组中的dp最大值进行求和,即为最终要保留的最大值。

那么dp转移方程就出来了,假设当前要判断的数为k,当前数总计出现了 cnt[k] 次

那么 dp[k][1] 表示当前数要保留,那面,前一个数一定不能保留,于是 dp[k][1] = dp[k-d][0] + cnt[k]

而 dp[k][0] 表示当前数要去除,则前一个数保留与否均可,取个最大值即可,即 dp[k][0] = max(dp[k-d][0],dp[k-d][1])

接下来看一下代码,我将原始求解写在了 first_pass_function() 函数中,将整理后的简易方法写在了 main() 函数中

/*
Author Owen_Q
*/
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;
const int M = 1e6 + 1;

int a[N];
int pre[M];
bool in[M];
int cnt[M];
set<int> col[M];
set<int> colSet;
// bool re[N];
int dp[M][2];



int findX(int x) {
    if (pre[x] == x) {
        return x;
    }
    return pre[x] = findX(pre[x]);
}

void unionXY(int x, int y) {
    int preX = findX(x);
    int preY = findX(y);
    if (preX > preY) {
        pre[x] = preY;
    } else if (preX < preY) {
        pre[y] = preX;
    }
}

int first_pass_function() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    int n, d;
    cin >> n >> d;
    int rere = 0;
    // 初始化
    for (int i = 0; i < M; i++) {
        pre[i] = i;
        in[i] = false;
        cnt[i] = 0;
        col[i].clear();
    }
    colSet.clear();
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        cnt[a[i]]++;
        in[a[i]] = true;
        if (cnt[a[i]] == 1) {
            if (a[i] - d >= 0 && in[a[i] - d]) {
                unionXY(a[i], a[i] - d);
            }
            if (a[i] + d < M && in[a[i] + d]) {
                unionXY(a[i], a[i] + d);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        int preA = findX(a[i]);
        col[preA].insert(a[i]);
        colSet.insert(preA);
    }
    for (auto colSetIt = colSet.begin(); colSetIt != colSet.end(); ++colSetIt) {
        int curPre = *colSetIt;
        int colSize = col[curPre].size();
        int colCnt = 0;
        // bool colStart = true;
        int colStartCnt = 0;
        memset(dp, 0, sizeof(dp));
        int nowPos = 1;
        for (auto colPreIt = col[curPre].begin(); colPreIt != col[curPre].end(); ++colPreIt) {
            int now = *colPreIt;
            // cout << now << "**" << endl;
            // 以下为间隔删除,该方法不正确,更换为dp
            // if (colStart) {
            //     colStart = false;
            // } else {
            //     colStart = true;
            // }
            // if (colStart) {
            //     colStartCnt += cnt[now];
            // }
            colCnt += cnt[now];

            dp[nowPos][1] = dp[nowPos-1][0] + cnt[now];
            dp[nowPos][0] = max(dp[nowPos-1][0], dp[nowPos-1][1]);
            nowPos ++;
        }
        colStartCnt = max(dp[colSize][0], dp[colSize][1]);
        // cout << colCnt << "*"  << colStartCnt <<  "\n";
        // 特判逻辑
        if (d == 0) {
            rere += colCnt - 1;
        } else if (colStartCnt > colCnt / 2) {
            rere += colCnt - colStartCnt;
            // colStart = true;
        } else {
            rere += colStartCnt;
            // colStart = false;
        }
        // cout << rere << "***" << endl;
        // colStartPre = -1;
        // for (auto colPreIt = col[curPre].begin(); colPreIt != col[curPre].end(); ++colPreIt) {
        //     int now = *colPreIt;
        //     if (now != colStartPre) {
        //         colStartPre = now;
        //         if (colStart) {
        //             colStart = false;
        //         } else {
        //             colStart = true;
        //         }
        //     }
        //     if (colStart) {
        //         re[now] = true;
        //     }
        // }
    }
    // int reDelCnt = 0;
    // for (int i = 1; i <= n; i++) {
    //     if (!re[a[i]]) {
    //        reDelCnt++;
    //     }
    // }

    cout << rere << endl;
    return 0;
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(nullptr);
    int n, d;
    cin >> n >> d;
    // 初始化
    memset(cnt, 0, sizeof(cnt));
    // memset(dp, 0, sizeof(dp));
    int difCnt = 0;
    int maxA = 0;
    for (int i = 1; i <= n; i++) {
        int A;
        cin >> A;
        cnt[A]++;
        if (cnt[A] == 1) {
            difCnt++;
            maxA = max(maxA, A);
        }
    }
    if (d == 0) {
        cout << n - difCnt << endl;
    } else {
        int maxRe = 0;
        for (int i = 0; i < d; i++) {
            // 第一轮dp初始化
            dp[i][1] = cnt[i];
            dp[i][0] = 0;
            int curRe = cnt[i];
            for (int j = i+d; j <= maxA; j+=d) {
                // 后续dp转移
                dp[j][1] = dp[j-d][0] + cnt[j];
                dp[j][0] = curRe;
                curRe = max(dp[j][0], dp[j][1]);
            }
            maxRe += curRe;
        }
        cout << n - maxRe << endl;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值