004-【编程】牛客大厂春招笔试复盘
苦练算法题,只为进面,唉。
编程题
小红的字母填写
题目
小红拿到了一排格子,每个格子的背景是红色或者蓝色。
小红希望你将每个格子上填写一个小写字母,需要满足相同的字母的背景颜色是相同的。
小红希望最终出现次数最多的字母的出现次数尽可能小。你能帮帮她吗?
输入描述:
一个仅由字符'0'和'1'组成的字符串,长度不超过200000。
字符串用于表示小红拿到的格子的颜色。第个字符为'0'代表第第个格子为蓝色背景,字符'1'代表红色背景。
输出描述:
一个仅由小写字母构成的字符串,第个字符为第个格子上填写的字母,请务必保证字符串是合法的。如果有多解,输出任意即可。
示例1
输入例子:
010
输出例子:
abc
例子说明:
'a'为蓝色,'b'为红色,'c'为蓝色。三种字母均只出现了一次
示例2
输入例子:
000000000000000000000000001
输出例子:
bbcdefghijklmnopqrstuvwxyza
例子说明:
我们这个填空方案中,两个'b'都是蓝色,符合题目要求。除了'b'出现2次以外,其余的字母均只出现了1次。
解析
求0和1的用于分配26个字母的比例,然后轮流进行分配。
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int main() {
string str;
cin >> str;
int zeroCnt = 0, oneCnt = 0;
for (char ch : str)
{
if (ch == '0')
{
++zeroCnt;
}
else {
++oneCnt;
}
}
cerr << "ZeroCnt: " << zeroCnt << endl;
cerr << "OneCnt:" << oneCnt << endl;
int zeroRadio = ceil(static_cast<float>(zeroCnt) / static_cast<float>(zeroCnt + oneCnt) * 25);
int oneRadio = ceil(static_cast<float>(oneCnt) / static_cast<float>(zeroCnt + oneCnt) * 25);
if (zeroCnt == 0)
oneRadio = 26;
else if (oneCnt == 0)
zeroRadio = 26;
int zeroOffset = 0;
int oneOffset = zeroRadio;
for (char ch : str)
{
if (ch == '0')
{
cout << static_cast<char>('a' + zeroOffset);
++zeroOffset;
if (zeroOffset >= zeroRadio)
{
zeroOffset = 0;
}
}
else
{
cout << static_cast<char>('a' + oneOffset);
++oneOffset;
if (oneOffset >= 26)
{
oneOffset = zeroRadio;
}
}
}
}
反思
我烂完了,这都想了快一小时。顺便,牛客OJ上可以用cerr进行DEBUG!。
小美的外卖订单编号
题目
美团商家的订单发起时,订单编号最开始从 1 开始,后续每发起一个订单,订单编号便在上一订单编号的基础上 +1。为了防止订单号过大,商家还可以设置一个编号上限m,当订单编号超过m时,将又从 1 开始编号。 小美想知道,当订单编号上限为m时,第x个订单编号是多少?将有q次询问。
输入描述:
第一行输入一个整数。
接下来行,每行两个整数。
输出描述:
行,每行一个整数表示答案。
示例1
输入例子:
4
2 3
5 17
8 2
4 4
输出例子:
1
2
2
4
解析
使用模运算即可,但注意模为0的情况:例如要获得上限2的第四个订单,1 2 1 2应该是2。
#include <iostream>
using namespace std;
int main() {
int q;
cin >> q;
while (q--)
{
int m, x;
cin >> m >> x;
if (x > m)
{
x = (x % m == 0) ? m : (x % m);
}
cout << x << endl;
}
}
// 64 位输出请用 printf("%lld")
小美种果树
题目
小美在手机上种果树,只要成熟了就可以领到免费的水果了。
小美每天可以给果树浇水,果树的成长值加 xx。同时也可以给果树施肥,两次施肥至少需要间隔 2 天,果树的成长值加 yy。果树成长值达到 zz 就成熟了。
小红想知道,最少需要多少天可以领到免费的水果。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
输入描述:
一行三个整数 ,分别表示浇水的成长值,施肥的成长值,果树成熟的成长值。
输出描述:
一行一个整数,表示最少需要多少天可以领到免费的水果。
示例1
输入例子:
1 2 10
输出例子:
6
例子说明:
第一天施肥浇水,成长值为 3。
第二天浇水,成长值为 3 + 1 = 4。
第三天浇水,成长值为 4 + 1 = 5。
第四天施肥浇水,成长值为 5 + 3 = 8。
第五天浇水,成长值为 8 + 1 = 9。
第六天浇水,成长值为 9 + 1 = 10。
果树成熟了,可以领到免费水果了!
解析
模拟题,注意处理浇水间距:
#include <iostream>
using namespace std;
int main()
{
int x, y, z;
cin >> x >> y >> z;
int lastWaterDay = -3;
int day = 0;
int score = 0;
while (score < z)
{
cerr << "Day " << day;
if (day - lastWaterDay == 3)
{
lastWaterDay = day;
cerr << " water, ";
score += y;
}
cerr << "fuel\n";
score += x;
++day;
}
cout << day;
}
// 64 位输出请用 printf("%lld")
小美的数组构造
题目
解析
应该使用动态规划来解题,dp[i][j]
的含义是前i
个数之和为j
的总方案数,状态转移方程为dp[i][j] = dp[i - 1][j - k1] + dp[i - 1][j - k2]...
。其中,为了满足条件1,有k != a[i - 1]
。
代码如下:
#include <iostream>
#include <vector>
using namespace std;
constexpr int MOD = 1e9 + 7;
int main() {
int n;
cin >> n;
int targetSum = 0;
vector<int> a(n);
for (int i = 0; i < n; ++i)
{
cin >> a[i];
targetSum += a[i];
}
// dp[i][j]: 前i个数之和为j 的构造方案数
// dp[i][j] = dp[i - 1][j - k1] + dp[i - 1][j - k2] + ...
vector<vector<int>> dp(n + 1, vector<int>(targetSum + 1, 0));
dp[0][0] = 1;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= targetSum; ++j)
{
for (int k = 1; k <= j; ++k)
{
if (k != a[i - 1])
{
dp[i][j] = (dp[i][j] + dp[i - 1][j - k]) % MOD;
}
}
}
}
cout << dp[n][targetSum];
}
// 64 位输出请用 printf("%lld")
反思
唉,不看别人解析写不出来。
小球投盒
题目
解析
#include <iostream>
#include <set>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int ans = 0;
set<int> doOp1, doOp2;
while (m--)
{
++ans;
int op, x;
cin >> op >> x;
if (op == 1)
{
doOp1.insert(x);
// insert(x)后, 都有球 ||
// insert(x)后, 并且除了x都有球
if (doOp1.size() >= n || doOp2.count(x))
{
cout << ans;
return 0;
}
}
else
{
doOp2.insert(x);
// 除了y,其他都有一个球, 并且insert(x)后除了x, 其他都有一个球 ||
// 除了x, 其他都有一个球,并且x有球
if (doOp2.size() > 1 || doOp1.count(x))
{
cout << ans;
return 0;
}
}
}
cout << -1;
}
// 64 位输出请用 printf("%lld")
反思
唉,阅读理解能力不行。
小美的树上染色
题目
解析
这道题是树形DP类型:
dp[u][0]
:不染色节点u的情况下,染色节点数最大值;dp[u][1]
:染色节点u的情况下,染色节点数最大值。
然后是状态转移方程:
dp[u][0] += max(dp[i][0], dp[i][1])
,其中i和u在一个图中。不染色u的情况下,染色节点数最大值为它周围子节点的染色节点数最大值之和。dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[i][0], dp[i][1]) + dp[i][0] + 2)
,其中i和u在一个图中。染色u的情况下,染色节点数最大值需要好好解释。由于题中限制若当前节点染色,那么它相邻的节点只能有一个染色,因此我们需要选择一个相邻节点,使得染色节点数最大。接下来是计算,在节点U不染色且节点I不染色(dp[u][0] - max(dp[i][0], dp[i][1]) + dp[i][0]
)的情况下,将两个节点都染色(+2
)。
代码如下:
#include <iostream>
#include <vector>
#include <list>
#include <cmath>
using namespace std;
struct Graph
{
vector<int> value;
vector<list<int>> g;
Graph(int n)
{
value.resize(n + 1);
g.resize(n + 1);
}
};
int main() {
int n;
cin >> n;
Graph graph(n);
for (int i = 1; i <= n; ++i)
{
cin >> graph.value[i];
}
for (int i = 0; i < n - 1; ++i)
{
int from, to;
cin >> from >> to;
graph.g[from].push_back(to);
graph.g[to].push_back(from);
}
// 树形DP:
// dp[u][0]: 不染红u情况下, 染红节点数最大值
// dp[u][0] = sum(max(dp[i][0], dp[i][1]), ...), 其中i和u在一个图中
// dp[u][1]: 染红u情况下, 染红节点数最大值
// dp[u][1] = max(dp[u][0] - max(dp[i][0], dp[i][1]) + dp[i][0] + 2), 其中i和u在一个图中
// U不染色 且 I不染色的情况下, 让U和I染色
vector<vector<int>> dp(n + 1, vector<int>(2));
auto DFS = [&](auto&& DFS, int u, int parent) -> void
{
// 先求dp[u][0]
for (int i : graph.g[u])
{
// 防止重复求值
if (parent == i)
{
continue;
}
DFS(DFS, i, u);
dp[u][0] += max(dp[i][0], dp[i][1]);
}
// 再求dp[u][1]
for (int i : graph.g[u])
{
// 防止重复求值
if (parent == i)
{
continue;
}
int root = static_cast<int>(sqrt(graph.value[i] * graph.value[u]));
if (root * root == graph.value[i] * graph.value[u])
{
dp[u][1] = max(dp[u][1], dp[u][0] - max(dp[i][0], dp[i][1]) + dp[i][0] + 2);
}
}
};
DFS(DFS, 1, 0);
cout << max(dp[1][0], dp[1][1]);
}
// 64 位输出请用 printf("%lld")
在代码实现中,需要注意的是将子节点和父节点进行比较,避免进行重复计算导致超时,或者结果错误。
反思
同样是不看题解写不出来,需要反复练习。。。