LintCode 1527: Majsoul on ! (枚举+DFS好题)

1527. Majsoul on !

 

According to a mahjong game called bird soul, simplified the rules of the game, as follows:

 

There are 36 cards in total, and each card is 1-9.And each number has four cards.

 

If 14 cards are taken out and the following conditions are satisfied, that is to win in mahjong:

 

Two of the 14 cards have the same number of cards, known as the Sparrow head.

Remove the above 2 cards, the remaining 12 cards can form 4 shunzi or kezi.

Note: shunzi means an increasing number of three consecutive characters (e.g. 234,567), and an kezi means three characters of the same number (e.g. 111,777).

 

You draw 13 cards out of 36 cards, and then you take another card out of the remaining 23 cards, and what kind of number cards can you draw with?

 

Example

Example 1:

 

Input: 

[1, 1, 1, 2, 2, 2, 5, 5, 5, 6, 6, 6, 9]

Output: 

[9]

Explanation:

It can be made up of four kezi of 1,2,5,6 and a head of a sparrow of 9.

Example 2:

 

Input: 

[1, 1, 1, 1, 2, 2, 3, 3, 5, 6, 7, 8, 9]

Output: 

[4, 7]

Explanation:

It can be made up of (123), (123), (567) and (456) or (789), and a head of a sparrow of 1.

Notice

If there are multiple cards that satisfied the conditions, please return them in the order from smallest to largest. If no card satisfied return 0.

解法1:枚举+DFS

这题我觉得并不容易。主要是对没玩过麻将的同学比较难,那些术语可能不熟悉。

思路:枚举1到9,每个数字加到cards里面后排序,看是否能胡。
而判断是否能胡这个过程是用DFS。DFS里面有个参数是has_sparrow,表示是否已有雀头。如果has_sparrow=TRUE,则DFS就只用判断
顺子和刻子,否则先判断是否有雀子,再判断是否有顺子和刻子。

注意:
1) 如果有雀头或刻子,都是直接从nums开头裁掉相应数字,然后继续递归(按有没有雀头)。但如果是顺子,则只能裁开头第1个数字,并将其后面(不一定直接相连)的顺子数字从vector中去掉。比如[1,1,1,2,2,2,3,3,3,5,7,7,9],顺子为1,2,3,而2和3并不予1相连。
2) 也可以不采用has_sparrow这个参数。网上有答案直接用nums.size()%3。因为没有雀头时,比如说14,11, 8等(每个由14减去若干个3,因为刻子和顺子都是3个) 都不能被3整除,去掉雀头后变成12,9,6等都可以被3整除。但是不是很直观。

class Solution {
public:
    /**
     * @param cards: A list of cards.
     * @return: A list of feasible solution.
     */
    vector<int> getTheNumber(vector<int> &cards) {
        int len = cards.size();
        vector<int> res;
        
        for (int i = 1; i <= 9; ++i) {
            vector<int> nums = cards;
            nums.push_back(i);
            if (count(nums.begin(), nums.end(), i) > 4) continue;
            sort(nums.begin(), nums.end());
            
            if (is_hu(nums, false)) {
                res.push_back(i);
            }
        }
        
        if (res.size() == 0) return {0};
        else return res;
    }

private:
    bool is_hu(vector<int> & nums, bool has_sparrow) {
        int len = nums.size();
        if (len == 0) return true;
        int head_repeat_num = 1;
        
        for (int i = 1; i < len; ++i) {
            if (nums[i] == nums[i - 1]) head_repeat_num++;
            else break;
        }

        //如果没有雀头,但起始重复数字>=2,可做雀头,
        if (!has_sparrow) {
            if (head_repeat_num >= 2) {
                vector<int> new_nums(nums.begin() + 2, nums.end());
                if (is_hu(new_nums, true)) return true;
            }
        }
        
        //如果起始重复元素>=3,可做刻子,去掉刻子
        if (head_repeat_num >= 3) {
            vector<int> new_nums(nums.begin() + 3, nums.end());
            if (is_hu(new_nums, false)) return true;
        }
        
        //如果第1个元素跟后面某2个元素是顺子, 去掉顺子,注意顺子不一定紧挨着
        if (count(nums.begin(), nums.end(), nums[0] + 1) > 0 &&
            count(nums.begin(), nums.end(), nums[0] + 2) > 0) {
        //        vector<int> new_nums(nums.begin() + 3, nums.end());
        //        if (is_hu(new_nums, false)) return true;
        	vector<int> new_num(nums.begin() + 1, nums.end());         
		    new_num.erase(find(new_num.begin(), new_num.end(), nums[0] + 1));
		    new_num.erase(find(new_num.begin(), new_num.end(), nums[0] + 2));
		    if (is_hu(new_num, false))             
			    return true;  
            }
        
        return false;
    }
};

解法2:枚举+循环遍历。即is_hu采用循环遍历的方法判断。

下次做。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值