
005.寻找最大的葫芦
难度:易
问题描述
在一场经典的德州扑克游戏中,有一种牌型叫做“葫芦”。“葫芦”由五张牌组成,其中包括三张相同牌面值的牌 aa 和另外两张相同牌面值的牌 bb。如果两个人同时拥有“葫芦”,我们会优先比较牌 aa 的大小,若牌 aa 相同则再比较牌 bb 的大小,牌面值的大小规则为:1 (A) > K > Q > J > 10 > 9 > ... > 2,其中 1 (A) 的牌面值为1,K 为13,依此类推。
在这个问题中,我们对“葫芦”增加了一个限制:组成“葫芦”的五张牌牌面值之和不能超过给定的最大值 maxmax。
给定一组牌,你需要找到符合规则的最大的“葫芦”组合,并输出其中三张相同的牌面和两张相同的牌面。如果找不到符合条件的“葫芦”,则输出 “0, 0”。
测试样例
样例1:
输入:
n = 9, max = 34, array = [6, 6, 6, 8, 8, 8, 5, 5, 1]
输出:[8, 5]
说明:array数组中可组成4个葫芦,分别为[6,6,6,8,8],[6,6,6,5,5],[8,8,8,6,6],[8,8,8,5,5]。其中[8,8,8,6,6]的牌面值为36,大于34不符合要求。剩下的3个葫芦的大小关系为[8,8,8,5,5]>[6,6,6,8,8]>[6,6,6,5,5],故返回[8,5]
样例2:
输入:
n = 9, max = 37, array = [9, 9, 9, 9, 6, 6, 6, 6, 13]
输出:[6, 9]
说明:可组成2个葫芦,分别为[9,9,9,6,6]和[6,6,6,9,9],由于[9,9,9,6,6]的牌面值为39,大于37,故返回[6,9]
样例3:
输入:
n = 9, max = 40, array = [1, 11, 13, 12, 7, 8, 11, 5, 6]
输出:[0, 0]
说明:无法组成任何葫芦,故返回[0,0]
样例4:
输入:
n = 6, max = 50, array = [13, 13, 13, 1, 1, 1]
输出:[1, 13]
说明:可组成两个葫芦,分别为[A,A,A,K,K]和[K,K,K,A,A],两者牌面值都小于50,故都合法。因为三张相同牌面值的A > K,故[A,A,A,K,K]比[K,K,K,A,A]要大,返回[1,13]
思路说明
题目要求在给定的牌中找到符合条件的最大“葫芦”组合,即三张相同牌面值的牌和两张相同牌面值的牌,且五张牌的牌面值之和不能超过给定的最大值 max。牌面值的大小规则为:A > K > Q > J > 10 > 9 > ... > 2。为了找到符合条件的最大“葫芦”组合,我们可以先统计每种牌面值的数量,然后通过双层循环遍历所有可能的牌面值组合,检查是否满足条件。
解题过程
- 统计牌面值数量:使用
Counter统计每种牌面值的数量。 - 遍历所有可能的牌面值组合:通过双层循环遍历所有可能的牌面值组合 (x,y),其中 x 代表三张相同牌面值的牌,y 代表两张相同牌面值的牌。
- 检查条件:对于每个组合 (x,y),检查是否满足以下条件:
- x×3+y×2≤max:五张牌的牌面值之和不超过最大值 max。
- c[x]≥3:牌面值为 x 的牌数量至少为 3。
- c[y]≥2:牌面值为 y 的牌数量至少为 2。
- x=y:三张相同牌面值的牌和两张相同牌面值的牌不能相同。
- 返回结果:如果找到符合条件的组合,返回 [x,y];否则返回 [0,0]。
代码提示
-
统计牌面值的频率:
首先,你需要统计每种牌面值出现的频率。可以使用一个字典来存储每个牌面值及其出现的次数。 -
筛选出可能的三张相同牌面值:
遍历字典,找出所有出现次数大于等于3的牌面值。这些牌面值可以作为“葫芦”中的三张相同牌面值。 -
筛选出可能的两张相同牌面值:
对于每一个可能的三张相同牌面值,再遍历字典,找出所有出现次数大于等于2的牌面值(不包括已经选中的三张相同牌面值)。这些牌面值可以作为“葫芦”中的两张相同牌面值。 -
计算牌面值之和并筛选符合条件的“葫芦”:
对于每一个可能的“葫芦”组合(三张相同牌面值和两张相同牌面值),计算它们的牌面值之和,并检查是否小于等于给定的最大值max。 -
选择最大的“葫芦”组合:
在所有符合条件的“葫芦”组合中,选择牌面值最大的组合。如果存在多个组合的牌面值相同,则优先选择三张相同牌面值较大的组合;如果三张相同牌面值相同,则选择两张相同牌面值较大的组合。 -
返回结果:
如果没有找到符合条件的“葫芦”组合,则返回[0, 0]。否则,返回符合条件的最大“葫芦”组合。
复杂度分析
- 时间复杂度:O(n+k2),其中 n 是牌的数量,k 是牌面值的种类数(本题中 k=13)。首先需要 O(n) 的时间统计每种牌面值的数量,然后需要 O(k2) 的时间遍历所有可能的牌面值组合。
- 空间复杂度:O(k),主要用于存储每种牌面值的数量。
知识点扩展
- 哈希表:在本题中,使用
Counter统计每种牌面值的数量,这是一种典型的哈希表应用。哈希表可以在 O(1) 的时间复杂度内完成插入和查找操作,非常适合用于统计和计数问题。 - 双层循环:通过双层循环遍历所有可能的牌面值组合,这是一种常见的暴力枚举方法。虽然时间复杂度较高,但在本题中由于牌面值种类数 k 较小,因此是可行的。
MarsCode AI答案
Python:
def solution(n, max, array):
# 统计牌面值的频率
frequency = {}
for card in array:
if card in frequency:
frequency[card] += 1
else:
frequency[card] = 1
# 筛选出可能的三张相同牌面值
possible_triplets = [card for card, count in frequency.items() if count >= 3]
# 初始化结果
best_triplet = 0
best_pair = 0
# 遍历所有可能的三张相同牌面值
for triplet in possible_triplets:
# 筛选出可能的两张相同牌面值
possible_pairs = [card for card, count in frequency.items() if count >= 2 and card != triplet]
# 遍历所有可能的两张相同牌面值
for pair in possible_pairs:
# 计算牌面值之和
total_value = triplet * 3 + pair * 2
# 检查是否符合条件
if total_value <= max:
# 更新最佳组合
if triplet > best_triplet or (triplet == best_triplet and pair > best_pair):
best_triplet = triplet
best_pair = pair
# 返回结果
if best_triplet == 0:
return [0, 0]
else:
return [best_triplet, best_pair]
if __name__ == "__main__":
# Add your test cases here
print(solution(9, 34, [6, 6, 6, 8, 8, 8, 5, 5, 1]) == [8, 5])
print(solution(9, 37, [9, 9, 9, 9, 6, 6, 6, 6, 13]) == [6, 9])
print(solution(9, 40, [1, 11, 13, 12, 7, 8, 11, 5, 6]) == [0, 0])
Go:
package main
import (
"fmt"
"sort"
)
func solution(n int, max int, array []int) []int {
// 统计每个牌面值的数量
count := make(map[int]int)
for _, num := range array {
count[num]++
}
// 将牌面值按从大到小排序
keys := make([]int, 0, len(count))
for k := range count {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
// 尝试找到符合条件的最大葫芦
for _, a := range keys {
if count[a] >= 3 {
for _, b := range keys {
if a != b && count[b] >= 2 {
if 3*a+2*b <= max {
return []int{a, b}
}
}
}
}
}
return []int{0, 0}
}
func main() {
// 添加测试用例
fmt.Println(solution(9, 34, []int{6, 6, 6, 8, 8, 8, 5, 5, 1})) // 应输出 [8, 5]
fmt.Println(solution(9, 37, []int{9, 9, 9, 9, 6, 6, 6, 6, 13})) // 应输出 [6, 9]
fmt.Println(solution(9, 40, []int{1, 11, 13, 12, 7, 8, 11, 5, 6})) // 应输出 [0, 0]
}
Rust:
fn solution(n: usize, max: i32, array: Vec<i32>) -> Vec<i32> {
use std::collections::HashMap;
// Step 1: 统计每种牌面值的出现次数
let mut count_map = HashMap::new();
for &card in &array {
*count_map.entry(card).or_insert(0) += 1;
}
// Step 2: 生成所有可能的“葫芦”组合
let mut valid_combos = Vec::new();
for (&a, &count_a) in &count_map {
if count_a >= 3 {
for (&b, &count_b) in &count_map {
if b != a && count_b >= 2 {
let sum = 3 * a + 2 * b;
if sum <= max {
valid_combos.push((a, b, sum));
}
}
}
}
}
// Step 3: 排序和选择最大组合
if valid_combos.is_empty() {
return vec![0, 0];
}
valid_combos.sort_by(|&(a1, b1, _), &(a2, b2, _)| {
if a1 != a2 {
a2.cmp(&a1) // 优先比较三张相同牌面值的大小
} else {
b2.cmp(&b1) // 再比较两张相同牌面值的大小
}
});
let (a, b, _) = valid_combos[0];
vec![a, b]
}
fn main() {
// Add your test cases here
println!("{:?}", solution(9, 34, vec![6, 6, 6, 8, 8, 8, 5, 5, 1]) == vec![8, 5]);
println!("{:?}", solution(9, 37, vec![9, 9, 9, 9, 6, 6, 6, 6, 13]) == vec![6, 9]);
println!("{:?}", solution(9, 40, vec![1, 11, 13, 12, 7, 8, 11, 5, 6]) == vec![0, 0]);
}
006.小E的怪物挑战
难度:中
问题描述
小E在一个游戏中遇到了nn个按顺序出现的怪物。每个怪物都有其特定的血量h[i]和攻击力a[i]。小E的初始血量为H,攻击力为A。游戏规则如下:
1.小E可以击败血量和攻击力都小于她当前属性的怪物
2.对于每只怪物,小E可以选择与它战斗或者跳过这只怪物
3.为了保持战斗节奏,要求击败的怪物序列中,后一个怪物的血量和攻击力都必须严格大于前一个怪物
小E想知道,她最多能击败多少怪物。
**输入 **
n:怪物的数量H:小E的血量A:小E的攻击力h[i]:第i个怪物的血量a[i]:第i个怪物的攻击力
**输出 **
- 返回小E最多能击败的怪物数量
**约束条件 **
1<n<1001<H,A,h[i],a[i]<1000
测试样例
样例1:
输入:
n = 3, H = 4, A = 5, h = [1, 2, 3], a = [3, 2, 1]
输出:1
样例2:
输入:
n = 5, H = 10, A = 10, h = [6, 9, 12, 4, 7], a = [8, 9, 10, 2, 5]
输出:2
样例3:
输入:
n = 4, H = 20, A = 25, h = [10, 15, 18, 22], a = [12, 18, 20, 26]
输出:3
样例4:
输入:
n = 4, H = 20, A = 25, h = [22, 18, 15, 10], a = [26, 20, 18, 12]
输出:1
思路说明
题目要求小E在初始血量和攻击力的情况下,最多能击败多少怪物。每个怪物都有血量和攻击力,小E只能击败血量和攻击力都小于她自身的怪物。击败一个怪物后,小E的血量和攻击力会变为该怪物的血量和攻击力。这个问题可以转化为一个最长递增子序列(LIS)问题,其中每个怪物的血量和攻击力可以看作是一个二维的点,我们需要找到一个递增的子序列,使得小E可以依次击败这些怪物。
解题过程
- 筛选初始可击败的怪物:首先,筛选出所有血量和攻击力都小于小E初始血量和攻击力的怪物,并将它们存储在一个列表中。
- 构建二维点对:将筛选出的怪物的血量和攻击力组成一个二维点对,存储在列表
t中。 - 动态规划初始化:创建一个长度为
m的数组f,其中m是筛选出的怪物数量,f[i]表示以第i个怪物结尾的最长递增子序列的长度。初始时,每个f[i]都为 1。 - 动态规划更新:遍历每个怪物
i,对于每个i,再遍历i之前的所有怪物j,如果t[j]的血量和攻击力都小于t[i]的血量和攻击力,则更新f[i]为f[j] + 1和f[i]的较大值。 - 返回结果:最终,返回
f数组中的最大值,即为小E最多能击败的怪物数量。
复杂度分析
- 时间复杂度:O(m2),其中 m 是筛选出的怪物数量。因为我们需要两层循环来更新动态规划数组
f。 - 空间复杂度:O(m),其中 m 是筛选出的怪物数量。我们使用了一个长度为
m的数组f来存储动态规划的结果。
知识点扩展
- 动态规划:动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。在这道题中,我们使用动态规划来求解最长递增子序列的长度。
- 最长递增子序列(LIS):最长递增子序列问题是一个经典的动态规划问题,通常可以通过动态规划或贪心算法来解决。在这道题中,我们将二维的点对问题转化为一维的LIS问题,从而简化了问题的求解。
- 贪心算法:虽然这道题主要使用动态规划,但贪心算法也可以用来解决LIS问题,特别是在某些情况下,贪心算法的时间复杂度可以优化到 O(nlogn)。
MarsCode AI答案
Python:
def solution(n: int, H: int, A: int, h: list, a: list) -> int:
# 初始化 dp 数组,长度为 n,初始值为 1
dp = [1] * n
# 遍历每个怪物 i
for i in range(n):
# 遍历所有之前的怪物 j
for j in range(i):
# 如果怪物 i 可以击败怪物 j,并且满足严格递增条件
if h[i] > h[j] and a[i] > a[j]:
# 更新 dp[i]
dp[i] = max(dp[i], dp[j] + 1)
# 返回 dp 数组中的最大值
return max(dp)
if __name__ == '__main__':
print(solution(3, 4, 5, [1, 2, 3], [3, 2, 1]) == 1)
print(solution(5, 10, 10, [6, 9, 12, 4, 7], [8, 9, 10, 2, 5]) == 2)
print(solution(4, 20, 25, [10, 15, 18, 22], [12, 18, 20, 26]) == 3)
Go:
package main
import (
"fmt"
"math"
)
func solution(n, H, A int, h, a []int) int {
// 初始化dp数组,dp[i]表示以第i个怪物结尾的序列中,小E最多能击败的怪物数量
dp := make([]int, n)
for i := 0; i < n; i++ {
dp[i] = 1
}
// 遍历每个怪物
for i := 0; i < n; i++ {
// 检查所有之前的怪物
for j := 0; j < i; j++ {
// 如果当前怪物可以被击败,并且满足血量和攻击力的严格递增条件
if h[j] < h[i] && a[j] < a[i] {
// 更新dp[i]
dp[i] = int(math.Max(float64(dp[i]), float64(dp[j]+1)))
}
}
}
// 找到dp数组中的最大值
max := 0
for i := 0; i < n; i++ {
if dp[i] > max {
max = dp[i]
}
}
return max
}
func main() {
fmt.Println(solution(3, 4, 5, []int{1, 2, 3}, []int{3, 2, 1}) == 1)
fmt.Println(solution(5, 10, 10, []int{6, 9, 12, 4, 7}, []int{8, 9, 10, 2, 5}) == 2)
fmt.Println(solution(4, 20, 25, []int{10, 15, 18, 22}, []int{12, 18, 20, 26}) == 3)
}
Rust:
fn solution(n: usize, H: i32, A: i32, h: Vec<i32>, a: Vec<i32>) -> i32 {
// 将怪物按照血量和攻击力排序
let mut monsters: Vec<(i32, i32)> = h.into_iter().zip(a.into_iter()).collect();
monsters.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
// dp[i] 表示以第i个怪物结尾的最长递增子序列的长度
let mut dp = vec![1; n];
for i in 1..n {
for j in 0..i {
if monsters[i].0 > monsters[j].0 && monsters[i].1 > monsters[j].1 {
dp[i] = dp[i].max(dp[j] + 1);
}
}
}
// 找到dp数组中的最大值
let max_len = dp.iter().max().unwrap();
*max_len as i32
}
fn main() {
println!("{}", solution(3, 4, 5, vec![1, 2, 3], vec![3, 2, 1]) == 1);
println!("{}", solution(5, 10, 10, vec![6, 9, 12, 4, 7], vec![8, 9, 10, 2, 5]) == 2);
println!("{}", solution(4, 20, 25, vec![10, 15, 18, 22], vec![12, 18, 20, 26]) == 3);
println!("{}", solution(4, 20, 25, vec![22, 18, 15, 10], vec![26, 20, 18, 12]) == 1);
}

被折叠的 条评论
为什么被折叠?



