最多牌组数

题目:麻将的游戏规则中,共有两种方式凑成「一组牌」:

顺子:三张牌面数字连续的麻将,例如 [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-2x张,i-1y张,dp[m][n]表示当留下剩余i-1m张,in张时的集合数,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[mi2][ni1]+(tshunzini)/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[mi1][ni]=max{dp[mi1][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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值