题目:麻将的游戏规则中,共有两种方式凑成「一组牌」:
顺子:三张牌面数字连续的麻将,例如 [4,5,6]
刻子:三张牌面数字相同的麻将,例如 [10,10,10]
给定若干数字作为麻将牌的数值(记作一维数组 tiles),请返回所给 tiles 最多可组成的牌组数。注意:凑成牌组时,每张牌仅能使用一次。
示例 1:
输入:tiles = [2,2,2,3,4]
输出:1
解释:最多可以组合出 [2,2,2] 或者 [2,3,4] 其中一组牌
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/Up5XYM
分析:
首先,我们如果拿到一组牌,如 1112223344,那么它可以组成111、234、234时为最多的牌组数,若是11122233最多即为111、222
也就是,当4算进来的时候,我们需要判断讲它与前面相连能否得到最多的集合
那么,我们假设当前计算的末尾为i
时,设其有t
张,i-2
有x
张,i-1
有y
张,dp[m][n]
表示当留下剩余i-1
的m
张,i
的n
张时的集合数,shunzi
为当前需要的顺子数(0 <= shunzi <= min{m,min{n,cnt}}
)
那么当前的集合数为:
n
e
w
_
s
c
o
r
e
=
s
h
u
n
z
i
+
d
p
[
m
i
−
2
]
[
n
i
−
1
]
+
(
t
−
s
h
u
n
z
i
−
n
i
)
/
3
new\_score= shunzi + dp[m_{i-2}][n_{i-1}] + (t-shunzi-n_i)/3\\
new_score=shunzi+dp[mi−2][ni−1]+(t−shunzi−ni)/3
d p [ m i − 1 ] [ n i ] = m a x { d p [ m i − 1 ] [ n i ] , n e w _ s c o r e } dp[m_{i-1}][n_i]=max\{dp[m_{i-1}][n_i],new\_score\} dp[mi−1][ni]=max{dp[mi−1][ni],new_score}
算法实现:
func maxGroupNumber(tiles []int) int {
count := make(map[int]int)
// 记录每张牌的数量
for _, tile := range tiles {
count[tile]++
}
var ks []int
for i, _ := range count {
ks = append(ks, i)
}
sort.Slice(ks, func(i, j int) bool {
return ks[i] < ks[j]
})
var dp [5][5]int // 表示预留x张t-2,y张t-1的时候 当前能组成的牌组
for i, ints := range dp {
for i2, _ := range ints {
dp[i][i2] = -1
}
}
dp[0][0] = 0
pre_tile := 0 // 当前牌的前一张的点数
for _, i := range ks {
cnt := count[i]
// 如果不连续
if pre_tile != i-1 {
dp00 := dp[0][0] // i-2 和 i-1 留下的牌数都为0,即使当前若不留牌的集合数
// 重新来过
for i1, ints := range dp {
for i2, _ := range ints {
dp[i1][i2] = -1
}
}
dp[0][0] = dp00
}
var new_dp [5][5]int
for i1, ints := range new_dp {
for i2, _ := range ints {
new_dp[i1][i2] = -1
}
}
for cnt_2 := 0; cnt_2 < 5; cnt_2++ { // cnt-2 的牌数
for cnt_1 := 0; cnt_1 < 5; cnt_1++ {
// 对应没那么多
if dp[cnt_2][cnt_1] < 0 {
continue
}
// 如果有满足,即使当 dp00时候
// 假设当顺子为shunzi。你们下一个new_2 = cnt_1 - shunzi; new_1 = cnt - shunzi
for shunzi := 0; shunzi <= min(cnt_1, min(cnt_2, cnt)); shunzi++ {
new_2 := cnt_1 - shunzi // 下一个的t-2,就是这个的t-1
// 新的t-1的范围为4和当前 cnt 减去被连续用剩下的
for new_1 := 0; new_1 <= min(4, cnt-shunzi); new_1++ {
new_score := dp[cnt_2][cnt_1] + shunzi + (cnt-shunzi-new_1)/3
new_dp[new_2][new_1] = max(new_dp[new_2][new_1], new_score)
}
}
}
}
dp = new_dp
pre_tile = i
}
ans := 0
for cnt_2 := 0; cnt_2 < 5; cnt_2++ {
for cnt_1 := 0; cnt_1 < 5; cnt_1++ {
ans = max(ans, dp[cnt_2][cnt_1])
}
}
return ans
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}