一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情。
题目链接:2212. 射箭比赛中的最大得分
题目描述
Alice 和 Bob 是一场射箭比赛中的对手。比赛规则如下:
- Alice 先射
numArrows支箭,然后 Bob 也射numArrows支箭。 - 分数按下述规则计算:
- 箭靶有若干整数计分区域,范围从
0到11(含0和11)。 - 箭靶上每个区域都对应一个得分
k(范围是0到11),Alice 和 Bob 分别在得分k区域射中ak和bk支箭。如果ak >= bk,那么 Alice 得k分。如果ak < bk,则 Bob 得k分 - 如果
ak == bk == 0,那么无人得到k分。
- 箭靶有若干整数计分区域,范围从
- 例如,Alice 和 Bob 都向计分为
11的区域射2支箭,那么 Alice 得11分。如果 Alice 向计分为11的区域射0支箭,但 Bob 向同一个区域射2支箭,那么 Bob 得11分。
给你整数 numArrows 和一个长度为 12 的整数数组 aliceArrows ,该数组表示 Alice 射中 0 到 11 每个计分区域的箭数量。现在,Bob 想要尽可能 最大化 他所能获得的总分。
返回数组 bobArrows ,该数组表示 Bob 射中 0 到 11 每个 计分区域的箭数量。且 bobArrows 的总和应当等于 numArrows 。
如果存在多种方法都可以使 Bob 获得最大总分,返回其中 任意一种 即可。
提示:
aliceArrows.length == bobArrows.length == 12sum(aliceArrows[i]) == numArrows
示例 1:
输入:numArrows = 9, aliceArrows = [1,1,0,1,0,0,2,1,0,1,2,0]
输出:[0,0,0,0,1,1,0,0,1,2,3,1]
解释:上表显示了比赛得分情况。
Bob 获得总分 4 + 5 + 8 + 9 + 10 + 11 = 47 。
可以证明 Bob 无法获得比 47 更高的分数。
示例 2:
输入:numArrows = 3, aliceArrows = [0,0,1,0,0,0,0,0,0,0,0,2]
输出:[0,0,0,0,0,0,0,0,1,1,1,0]
解释:上表显示了比赛得分情况。
Bob 获得总分 8 + 9 + 10 = 27 。
可以证明 Bob 无法获得比 27 更高的分数。
整理题意
题目给定 numArrows 支箭和长度为 12 的整数数组 aliceArrows(下标从 0 开始),aliceArrows[i] 表示在得分区域 i 上 Alice 的箭数,我们需要帮助 Bob 计算最大总得分,对于任意一个得分区域 i ,需要大于 aliceArrows[i] 即可得到 i 分,现在问使用 numArrows 支箭最大能够得到多少分。
如果存在多种方法都可以使 Bob 获得最大总分,返回其中 任意一种 即可。
解题思路分析
习惯性动作,首先看题目数据范围,箭的数量最大为 ,得分区域为 12 个。
由于总共只有 12 个得分区域(0 到 11 分),对于每个得分区域有得分和不得分两种情况,那么总共就有 种情况,总的情况数量较小,我们可以枚举所有可能的情况,分别判断每种情况对应的每个得分区域的得分情况和所需箭的总数,记录满足条件下的最大得分状态即可。
对于状态的枚举方法,可以选择 DFS 递归深搜枚举,由于总共有 种情况,我们还可以想到使用 二进制枚举 的方法。对于二进制中第 i 位来说,0 表示在得分为 i 的区域中不得分,为 1 则代表在该区域得分。
枚举过程中维护最大得分以及最大得分所对应的状态,最后根据最大得分所对应的状态进行相应的答案输出即可。
具体实现
- 遍历 ,其中每个整数的二进制表示为一种得分状态。
- 对于每种得分状态我们需要判断所需的最少箭数,因为需要大于
aliceArrows[i]即可得到i分,所以我们为了使用尽可能少的箭术,对于第i个区域,我们只需要使用aliceArrows[i] + 1支箭即可。 - 判断当前状态下最少使用的箭数量是否小于给定的
numArrows,当小于numArrows时表示满足条件,此时记录最大得分以及得分状态。 - 最后根据最大得分状态进行构造答案所需的箭数数组。
需要注意的是如果还有剩余的箭,我们需要将其添加到任意区域即可。
复杂度分析
- 时间复杂度:,其中
n为箭靶的数量,在本题中n = 12。所有的得分状态共有 种,对于单个状态,判断是否可行以及维护最大得分的时间复杂度为 。 - 空间复杂度:,利用整数记录二进制状态以及最大得分情况仅需常数空间。
如果使用
DFS递归做法,递归深度为箭靶的数量n,需要 的空间复杂度。
代码实现
class Solution {
public:
vector<int> maximumBobPoints(int numArrows, vector<int>& aliceArrows) {
int n = aliceArrows.size();
//cnt 记录当前状态所需要的箭数量
int cnt;
//score 记录当前得分
int score;
//maxScore 记录最大得分
int maxScore = 0;
//state 记录最大得分情况下的 mask (二进制枚举状态)
int state = 0;
//枚举所有得分情况,用二进制枚举,0表示不得分,1表示得分
for(int mask = 0; mask < (1 << n); mask++){
cnt = 0;
score = 0;
for(int j = 0; j < n; j++){
//当前状态下第j位为1表示Bob赢
if((mask >> j) & 1){
//需要比Alice多一支箭
cnt += aliceArrows[n - j - 1] + 1;
//记录得分
score += (n - j - 1);
}
}
//如果使用的箭小于总箭数,且得分更高就记录答案
if(cnt <= numArrows && score > maxScore){
maxScore = score;
state = mask;
}
}
//输出的答案数组
vector<int> ans;
ans.clear();
ans.resize(n);
//处理最后得到的最大分数所对应的答案
for(int i = 0; i < n; i++){
//该得分区域Bob获胜得分
if((state >> i) & 1){
//记录答案并将总的箭数减去相应的值
ans[n - i - 1] = aliceArrows[n - i - 1] + 1;
numArrows -= ans[n - i - 1];
}
//不得分区域不射箭
else ans[n - i - 1] = 0;
}
//如果有剩余的箭就全部放在任意一块区域都可
ans[0] += numArrows;
return ans;
}
};
总结
该题核心思路在于 枚举状态 ,采用二进制枚举的方法可以使得空间复杂度降为 O(1),在情况数量较小的情况下,我们可以使用更优的二进制枚举方法进行枚举。更一般的,二进制枚举是在状态为 2 的情况下使用,即选或不选、得分或不得分等,我们可以推广到三进制、四进制……等更多的情况下可应对更多不同的情形。
结束语
每个人的心里,都藏着一个了不起的自己。只要你不颓废、不消极,一直酝酿着乐观,培养着豁达,坚持着善良,始终朝着梦想前行,就没有到达不了的远方。