百度 秋招 2023.10.10 编程题目与题解

1、小红的精灵

小红有 n 个精灵,精灵分布在 m 个房间中,第 i 个精灵可以施展 ai 次魔法,现在有 k 个敌人要攻击,每次攻击需要一个房间的精灵都消耗一次魔法,该次攻击可以击杀一个敌人。问小红最多可以消灭多少敌人。

输入

一行三个整数 n,m,k,分别表示精灵数量,房间数量,敌人数量。

接下来 n 行,每行两个整数,第 i 行表示第 i 个精灵的所在房间编号 bi 和可以施展的魔法次数 ai

  • 1n,m105
  • 1bim
  • 1ai,k106

输出

输出一个整数,表示最多可以消灭的敌人数量。

样例

1
2
3
4
5
6
7
8
9
10
11
12
输入:
4 5 12
2 4
5 2
3 6
2 3

输出:
11

提示:
有12个敌人,第二个房间可以消灭3个敌人,第三个房间可以消灭6个敌人,第五个房间可以消灭2个敌人。总共可以消灭11个敌人。

解答

如果第 i 个房间没有精灵,那么第 i 个房间并没有用处。如果第 i 个房间有精灵,那么这个房间可以击败的敌人数为 ci=min1jn,bj=i{aj}

最终把 ci 的值进行求和即可,并和 k 取较小值。如果第 i 个房间没有精灵,那么令 ci=0

代码

1
2
3
4
5
6
7
8
n, m, k = map(int, input().split())
mp = {}
INF = 10 ** 9
for _ in range(n):
b, a = map(int, input().split())
mp[b] = min(mp.get(b, INF), a)
ans = min(sum(mp.values()), k)
print(ans)

2、小红分糖果

小红有 n 个糖果,要分给小朋友,每个小朋友必须分到 [l,r] 个,如果可以分完,输出最少可以分给多少小朋友,最多可以分给多少小朋友,如果不能分完,输出 1

输入

一行三个整数 n,l,r,表示糖果数量和每个小朋友的要求。

  • 1n109
  • 1lr109

输出

如果可以分完,输出最少可以分给多少小朋友,最多可以分给多少小朋友,如果不能分完,输出 1

样例

1
2
3
4
5
6
7
8
输入:
10 2 3

输出:
4 5

提示:
最少可以分给4个小朋友,[2,2,3,3]; 最多可以分给5个小朋友,每个小朋友分到2个糖果。

解答

假设现在需要求最大值,基本思想是每个小朋友都分的最少。也就是说,分得 d=n/l 个小朋友,还剩下 q=nld 个糖果。容易知道 d 是答案的一个上界,d 不能够再大了。可见 q<l,那么这些多出来的 q 个糖果需要分配回去,但是为了确保满足题意,哪怕分回去每个小朋友也不能分超过 r 个糖果,他们现在最多只能再得到 rl 个糖果。因此,剩下的糖果数 q 必须满足 qd(rl)。如果不满足 qd(rl)

那么如果分给 d 个小朋友不可行,有没有可能通过减少小朋友从而获得一个分配方案呢?答案是不可能的,因为减少了小朋友后,就要承担原本分给他的 l 个糖果,之前的小朋友反而还需要多承受一些糖果(总共剩余不止 q 糖果了)。因此减少小朋友并不能获得一个合法的分配方案,上面给出的答案就是最佳答案。

假设现在需要求最小值,思想和上面的类似,每个小朋友都分的最多。也就是说,分得 d=n/r 个小朋友,还剩下 q=nrd 个糖果。容易知道 d 是答案的一个下界,d 不能够再小了。类似的,可见 q<r。如果 q=0,那么这些糖果恰好分完,不需要再判断其它情况。如果 q>0,那么还需要多一个小朋友来承受这些糖果。如果 ql,那么说明这时新的小朋友已经满足了条件,此时答案为 d+1,如果 q<l,那么前面的小朋友只能都拿出 rl 个糖果给他。因此,如果 q+d(rl)l 仍然成立,那么这个小朋友也是符合条件的,否则他无法取到 l 个糖果,并不可行。

同样的问题,如果分给 d 个小朋友不可行,有没有可能通过增加小朋友从而获得一个分配方案呢?答案同样也是不可行的,因为增加了小朋友后,其余的小朋友就要拿出更多的糖果分出来,原来已经不够分配给持有 q 个糖果的小朋友的数量,现在更加不可行。因此增加小朋友并不能获得一个合法的分配方案,上面给出的答案就是最佳答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
n, l, r = map(int, input().split())
rp, rr = divmod(n, l)
if rp * (r - l) < rr:
rp = -1
lp, lr = divmod(n, r)
if lr > 0:
if lp * (r - l) + lr < l:
lp = -1
else:
lp += 1
if rp == -1:
print(-1)
else:
print(lp, rp)

3、小红的树上游走

小红有一棵 n 个节点的树,节点编号为 1 n。每条边有一个权值,权值是 1 或者 0

对于一个长度为 k 的节点序列 [a1,a2,,ak],按照顺序依次访问这些节点,从 ai 走到 ai+1,会按照最短路径走。

小红想知道,对于所有长度为 k 的节点序列中(一共有 nk 个长度为 k 的序列),有多少个序列满足:小红走完这个序列后,经过的所有边的权值之和不为 0。

由于答案可能很大,你只需要输出答案对 109+7 取模的结果。

注意,小红可以从一个节点走到它自己,也可以重复经过一条边。

输入

一行两个整数 n k,表示树的节点数和序列长度。

接下来 n1 行,每行三个整数 u,v,w,表示节点 u 和节点 v 之间有一条边,权值为 w

  • 1kn105
  • 1u,vn,0w1

输出

输出一个整数,表示满足条件的序列个数对 109+7 取模的结果。

样例

1
2
3
4
5
6
7
8
9
10
输入:
3 2
1 2 1
2 3 0

输出:
4

提示:
四个序列分别为[1,2],[2,1],[1,3],[3,1]

解答

这道题的对立问题是:有多少序列,经过的所有边的权值之和等于 0。最终用总方案数 nk 减去对立问题的答案数,就能过得到原问题的答案。

可以发现,求解对立问题的解比求解原问题要简单的多,只要序列 a 相邻的两个节点之间的路径都为 0 即可。原问题与此的区别在于,a至少存在一对相邻节点,其路径权值和不等于 0,由于还要考虑其它相邻对节点是否满足这个情况,因此直接求解原问题比较困难。

接下来求解这个对立问题。可见,只需要每一对相邻对节点的路径和为 0 即可,这时不需要考虑其它相邻节点对的问题,比较简单,我们使用并查集进行解决。将所有权值为 0 的所有边的相邻节点合并在一起,那么节点 u 和节点 v 的路径之和为 0 当且仅当它们在同一个集合中。因此,一个满足对立问题的序列,要求它的所有结点都在同一个集合中。假设所有合并操作完成后,一共有 m 个集合,第 i 个集合一共有 ci 个节点,那么这个集合中,满足的节点序列答案为 cik。因此本题的最终答案为 nki=1mcik

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
n, k = map(int, input().split())
fa = [i for i in range(n)]
sz = [1 for _ in range(n)]
mod = 10 ** 9 + 7


def find(x):
if fa[x] == x:
return x
fa[x] = find(fa[x])
return fa[x]


def merge(x, y):
u, v = find(x), find(y)
if u != v:
fa[v] = u
sz[u] += sz[v]


for _ in range(n - 1):
x, y, w = map(int, input().split())
x, y = x - 1, y - 1
if w == 0:
merge(x, y)

ans = (pow(n, k, mod) - sum(pow(sz[i], k, mod) for i in range(n) if i == find(i))) % mod
print(ans)
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
Ujimatsu Chiya 微信 微信
Ujimatsu Chiya 支付宝 支付宝