病毒检测
题目描述
小明最近在做病毒自动检测,他发现,在某些 $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$,碰撞结果为
- 如果 $x==y$,碰撞结果为两个石头消失。
- 如果 $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$。对每个蓄水池节点来说,他的儿子蓄水池节点都摆放在他的下面,并且和它用水管相连,根据重力,水会向下流动。现在我们要在蓄水池上做一些操作:
- 把节点 $v$ 填满水。然后v的所有儿子节点水也会被填满
- 清空节点 $v$ 的水。然后v所有的父亲节点水都会被清空
- 询问每个蓄水池节点是否有水。
初始状态时候,每个节点都是空的。
现在我们会依次进行一系列操作,我们想提前知道每次操作后的结果,你能帮忙解决吗?
代码实现
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语法,查看详细题解请移步至 A 或 B 或 C。
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
题目来源:快手2020校园招聘秋招笔试--工程C试卷。
文章声明:题目来源 牛客 平台,如有侵权,请联系删除!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。