头图

病毒检测

题目描述

小明最近在做病毒自动检测,他发现,在某些 $library$ 的代码段的二进制表示中,如果包含子串并且恰好有 $k$ 个$1$,就有可能有潜在的病毒。$library$ 的二进制表示可能很大,并且子串可能很多,人工分析不可能,于是他想写个程序来先算算到底有多少个子串满足条件。如果子串内容相同,但是开始或者结束位置不一样,则被认为是不同的子串。

注:子串一定是连续的。例如"010"有 $6$ 个子串,分别是 "0, "1", "0", "01", "10", "010"。

代码实现

long long solve(string s, int k) {
    typedef long long ll;
    int n = s.length();
    int idx[n + 1], m = 0;
    // 预处理出所有1的下标
    // 通过“相邻”两个1下标得到这两个1之间的0的个数
    for (int i = 1; i <= n; i++)
        if (s[i - 1] == '1')idx[++m] = i;
    idx[0] = 0, idx[m + 1] = n + 1;
    ll res = 0;
    // k为0时特殊处理
    if (k == 0) {
        for (int i = 1; i <= m + 1; i++) {
            ll x = idx[i] - idx[i - 1] - 1;
            res += (x + 1) * x / 2;
        }
    } else {
        for (int l = 1, r = l + k - 1; r <= m; l++, r++)
            res += (idx[l] - idx[l - 1]) * (idx[r + 1] - idx[r]);
    }
    return res;
}

优化实现

long long solve(string s, int k) {
    typedef long long ll;
    ll res = 0;
    int dp[k + 1], m = 0;
    dp[0] = 1;
    for (char c: s) {
        if (c == '1')m++, dp[m % (k + 1)] = 0;
        if (m >= k)res += dp[(m - k) % (k + 1)];
        // 统计第m到第m+1个1之间0的数量(数量含第m个1本身)
        dp[m % (k + 1)]++;
    }
    return res;
}

考试成绩

题目描述

现在你的班级刚刚参加了一个只有单选题的考试。班级一共 $n$ 个学生,考试有 $m$ 个问题。每个题目都有 $5$ 个可选答案($A$,$B$,$C$,$D$,$E$)。并且每个题目只有一个正确答案。每个题目的分数并不一样,第 $i$ 个题目的分数用 $a_i$ 表示。如果题目没答对该题会获得 $0$ 分。
考试结束后,每个学生都记得自己的答案,但是他们还不知道正确答案是什么。如果非常乐观的考虑,他们班级最多可能得到多少分呢?

代码实现

/**
 * @param ps 每位同学每道题的答案
 * @param a 每道题的分数
 */
long long solve(vector<string> ps, vector<int> a) {
    int m = a.size(), cnt[m + 1][5];
    memset(cnt, 0, sizeof cnt);
    for (auto &s: ps)
        for (int i = 0; i < m; i++)
            cnt[i][s[i] - 'A']++;
    long long res = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 1; j < 5; j++)
            if (cnt[i][j] > cnt[i][0])cnt[i][0] = cnt[i][j];
        res += a[i] * cnt[i][0];
    }
    return res;
}

石头碰撞

题目描述

给定一组石头,每个石头有一个正数的重量。每一轮开始的时候,选择两个石头一起碰撞,假定两个石头的重量为 $x,y$,$x \leq y$,碰撞结果为

  1. 如果 $x==y$,碰撞结果为两个石头消失。
  2. 如果 $x != y$,碰撞结果两个石头消失,生成一个新的石头,新石头重量为 $y-x$。

最终最多剩下一个石头为结束,问最小的剩余石头质量的可能是多少?

代码实现

int main() {
    int n, sum = 0;
    scanf("%d", &n);
    int a[n + 1];
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        sum += a[i];
    }
    int k = sum >> 1;
    int dp[k + 1];
    memset(dp, 0, sizeof dp);
    for (int i = 1; i <= n; i++) {
        for (int j = k; j >= a[i]; j--) {
            dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
        }
    }
    printf("%d", sum - (dp[k] << 1));
    return 0;
}

蓄水池大作战

题目描述

在你面前有 $n$ 个蓄水池,他们组成了树形结构(由 $n-1$ 条边连接)。蓄水池节点编号从 $1$ 开始到 $n$。对每个蓄水池节点来说,他的儿子蓄水池节点都摆放在他的下面,并且和它用水管相连,根据重力,水会向下流动。现在我们要在蓄水池上做一些操作:

  1. 把节点 $v$ 填满水。然后v的所有儿子节点水也会被填满
  2. 清空节点 $v$ 的水。然后v所有的父亲节点水都会被清空
  3. 询问每个蓄水池节点是否有水。

初始状态时候,每个节点都是空的。

现在我们会依次进行一系列操作,我们想提前知道每次操作后的结果,你能帮忙解决吗?

代码实现

void dfs(map<int, vector<int>> &mp, vector<int> &v, int x, int m) {
    v[x] = m;
    for (int i = 0; i < mp[x].size(); i++)
        dfs(mp, v, mp[x][i], m);
}

int main() {
    int n, a, b, q;
    scanf("%d", &n);
    map<int, vector<int>> son, pat;
    vector<int> v(n + 1, 0);
    while (--n) {
        scanf("%d%d", &a, &b);
        if (a > b) swap(a, b);
        son[a].push_back(b);
        pat[b].push_back(a);
    }
    scanf("%d", &q);
    while (q--) {
        scanf("%d%d", &a, &b);
        if (a == 1) dfs(son, v, b, 1);
        else if (a == 2) dfs(pat, v, b, 0);
        else printf("%d\n", v[b]);
    }
    return 0;
}

END

详细题解:由于本平台不能完全支持Markdown语法,查看详细题解请移步至 ABC

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。

题目来源:快手2020校园招聘秋招笔试--工程C试卷

文章声明:题目来源 牛客 平台,如有侵权,请联系删除!


字节幺零二四
9 声望5 粉丝

talk is cheap, show me you code!