#P4287. 第3题-猜拳
-
1000ms
Tried: 7
Accepted: 2
Difficulty: 4
所属公司 :
中国电信
时间 :25年10月25日场
-
算法标签>博弈论贪心
第3题-猜拳
解题思路
-
把 Bob 的出拳看成三类:出 0、出 1、出 2,分别计数为
cntB[0..2]。 -
Alice 手里分别有
a个 0、b个 1、c个 2(总和等于n)。 胜负规则是:1 胜 0,2 胜 1,0 胜 2。 -
为了让胜场最大,显然应当把 Alice 能赢的对局尽量“按类型匹配”:
- Bob 出 0 时,用 Alice 的 1 去赢:可赢
min(cntB[0], b)场; - Bob 出 1 时,用 Alice 的 2 去赢:可赢
min(cntB[1], c)场; - Bob 出 2 时,用 Alice 的 0 去赢:可赢
min(cntB[2], a)场。
- Bob 出 0 时,用 Alice 的 1 去赢:可赢
-
这是一个按类别独立的贪心/最大匹配问题;因为同类回合在顺序上等价,只需按数量配对即可。 答案为三项之和。
复杂度分析
- 统计次数与计算答案均为一次线性遍历:
时间复杂度
O(n)(每组数据),空间复杂度O(1)。
代码实现
Python
# 题面功能函数:根据 Bob 的计数和 Alice 的库存求最大胜场
def max_wins(a, b, c, cntB):
# 对应关系:B=0 用 1 赢;B=1 用 2 赢;B=2 用 0 赢
return min(cntB[0], b) + min(cntB[1], c) + min(cntB[2], a)
def main():
import sys
input = sys.stdin.readline
T = int(input().strip())
ans = []
for _ in range(T):
n = int(input().strip())
# 读取 Bob 的序列:既兼容"01201"也兼容"0 1 2 0 1"
seq_line = input().strip()
seq_digits = [ch for ch in seq_line if ch in '012']
# 若某些评测把这行拆分了,继续读到 n 个字符为止(保险处理)
while len(seq_digits) < n:
more = input().strip()
seq_digits.extend([ch for ch in more if ch in '012'])
a, b, c = map(int, input().split())
cntB = [0, 0, 0]
for ch in seq_digits[:n]:
cntB[ord(ch) - ord('0')] += 1
ans.append(str(max_wins(a, b, c, cntB)))
print("\n".join(ans))
if __name__ == "__main__":
main()
Java
import java.util.*;
/* ACM 风格,类名固定为 Main */
public class Main {
// 题面功能函数:根据 Bob 的计数和 Alice 的库存求最大胜场
static int maxWins(int a, int b, int c, int[] cntB) {
// B=0 用 1 赢;B=1 用 2 赢;B=2 用 0 赢
return Math.min(cntB[0], b) + Math.min(cntB[1], c) + Math.min(cntB[2], a);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
StringBuilder out = new StringBuilder();
while (T-- > 0) {
int n = sc.nextInt();
// 读取 Bob 的序列:既兼容 "01201" 也兼容 "0 1 2 0 1"
int[] cntB = new int[3];
int got = 0;
while (got < n) {
String tok = sc.next(); // 读取下一个非空白 token
for (int i = 0; i < tok.length() && got < n; i++) {
char ch = tok.charAt(i);
if (ch >= '0' && ch <= '2') {
cntB[ch - '0']++;
got++;
}
}
}
int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();
out.append(maxWins(a, b, c, cntB)).append('\n');
}
System.out.print(out.toString());
sc.close();
}
}
C++
#include <bits/stdc++.h>
using namespace std;
// 题面功能函数:根据 Bob 的计数和 Alice 的库存求最大胜场
int max_wins(int a, int b, int c, const array<int,3>& cntB) {
// B=0 用 1 赢;B=1 用 2 赢;B=2 用 0 赢
return min(cntB[0], b) + min(cntB[1], c) + min(cntB[2], a);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
if (!(cin >> T)) return 0;
while (T--) {
int n;
cin >> n;
// 读取 Bob 的序列:按字符读,自动跳过空白,兼容两种格式
array<int,3> cntB{0,0,0};
int got = 0;
while (got < n) {
char ch;
cin >> ch; // 读取下一个非空白字符
if (ch >= '0' && ch <= '2') {
cntB[ch - '0']++;
got++;
}
}
int a, b, c;
cin >> a >> b >> c;
cout << max_wins(a, b, c, cntB) << "\n";
}
return 0;
}
题目内容
Alice 与 Bob 进行 n 回合猜拳,出拳用数字表示:0、1、2,其中 1 打败 0 ,2 打败 1,0 打败 2 (其他情况视为 Alice 不胜)。
你已知 Bob 接下来每一回合的出拳序列。Alice 事先准备了 a 个 0、b 个 1、c 个 2 (保证 a+b+c=n ),每回合必须恰好出一拳,且各拳种的使用次数分别不超过 a,b,c 。
请你在这些限制下,合理安排 Alice 每一回合的出拳顺序,使得赢的回合数最大,并输出最多能赢的回合数。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≤T≤105) 代表数据组数,每组测试数据描述如下:
第一行输入一个整数 n(1≤n≤105) 。
第二行输入 n 个整数 s1,...,sn(si∈{0,1,2}).表示 Bob 每回合的出拳。
第三行输入三个整数 a,b,c , 表示 Alice 拥有的 0,1,2 的数量,保证 a+b+c=n 。
保证所有测试中 n 的总和不超过 2×105 。
输出描述
输出 T 行,每行一个整数,表示在最优安排下 Alice 最多能赢的回合数。
样例1
输入
3
5
0 1 2 0 1
2 2 1
3
2 2 2
3 0 0
4
0 1 2 2
1 1 2
输出
4
3
3