解题思路

这是一个回溯搜索问题。需要枚举所有可能的第14张牌,并判断是否能够和牌。

关键点:

  1. 枚举所有可能的第14张牌
  2. 判断是否能组成雀头
  3. 判断剩余牌是否能组成顺子或刻子
  4. 结果需要排序并去重

算法步骤:

  1. 统计当前手牌数量
  2. 尝试添加每种可能的牌
  3. 寻找可能的雀头
  4. 递归判断剩余牌是否能和牌

代码

#include <bits/stdc++.h>
using namespace std;

class MahjongSolver {
private:
    // 查找不同模式下的可用牌组合
    vector<string> findCandidates(const string& tiles, int mode) {
        // 统计牌的数量
        vector<int> counts(10, 0);
        for (char tile : tiles) {
            counts[tile - '0']++;
        }
        
        vector<string> candidates;
        
        if (mode == 0) {  // 查找可添加的牌
            for (int i = 1; i <= 9; i++) {
                if (counts[i] < 4) {
                    candidates.push_back(to_string(i));
                }
            }
        } else if (mode == 1) {  // 查找雀头
            for (int i = 1; i <= 9; i++) {
                if (counts[i] >= 2) {
                    string pair(2, char('0' + i));
                    candidates.push_back(pair);
                }
            }
        } else {  // 查找顺子和刻子
            // 查找刻子
            for (int i = 1; i <= 9; i++) {
                if (counts[i] >= 3) {
                    string triplet(3, char('0' + i));
                    candidates.push_back(triplet);
                }
            }
            // 查找顺子
            for (int i = 1; i <= 7; i++) {
                if (counts[i] > 0 && counts[i+1] > 0 && counts[i+2] > 0) {
                    string straight;
                    straight += char('0' + i);
                    straight += char('0' + i + 1);
                    straight += char('0' + i + 2);
                    candidates.push_back(straight);
                }
            }
        }
        
        return candidates;
    }
    
    // 移除指定牌型
    string removeTiles(string tiles, const string& pattern) {
        for (char tile : pattern) {
            auto pos = tiles.find(tile);
            if (pos != string::npos) {
                tiles.erase(pos, 1);
            }
        }
        return tiles;
    }
    
    // 判断是否能和牌
    bool canWin(const string& tiles) {
        if (tiles.empty()) {
            return true;
        }
        
        vector<string> patterns = findCandidates(tiles, 2);
        for (const string& pattern : patterns) {
            string remaining = removeTiles(tiles, pattern);
            if (canWin(remaining)) {
                return true;
            }
        }
        return false;
    }
    
public:
    string solve(string tiles) {
        // 移除空格
        tiles.erase(remove_if(tiles.begin(), tiles.end(), ::isspace), tiles.end());
        
        set<string> winningTiles;
        vector<string> candidates = findCandidates(tiles, 0);
        
        // 尝试每种可能的第14张牌
        for (const string& tile : candidates) {
            string newTiles = tiles + tile;
            // 尝试每种可能的雀头
            for (const string& pair : findCandidates(newTiles, 1)) {
                string remaining = removeTiles(newTiles, pair);
                if (canWin(remaining)) {
                    winningTiles.insert(tile);
                    break;
                }
            }
        }
        
        if (winningTiles.empty()) {
            return "0";
        }
        
        // 构建输出字符串
        string result;
        for (const auto& tile : winningTiles) {
            if (!result.empty()) {
                result += " ";
            }
            result += tile;
        }
        return result;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    string tiles;
    getline(cin, tiles);
    
    MahjongSolver solver;
    cout << solver.solve(tiles) << endl;
    
    return 0;
}
import java.util.*;

class MahjongSolver {
    private static final String[] NUMBERS = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
    
    // 生成重复字符串的辅助方法
    private String repeatString(String str, int times) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < times; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
    
    // 查找不同模式下的可用牌组合
    private List<String> findCandidates(String tiles, int mode) {
        // 统计牌的数量
        Map<Character, Integer> counts = new HashMap<>();
        for (char tile : tiles.toCharArray()) {
            counts.put(tile, counts.getOrDefault(tile, 0) + 1);
        }
        
        // 获取排序后的牌
        List<Character> sortedTiles = new ArrayList<>(counts.keySet());
        Collections.sort(sortedTiles);
        
        List<String> candidates = new ArrayList<>();
        
        if (mode == 0) {  // 查找可添加的牌
            for (String num : NUMBERS) {
                char tile = num.charAt(0);
                if (!counts.containsKey(tile) || counts.get(tile) < 4) {
                    candidates.add(num);
                }
            }
        } else if (mode == 1) {  // 查找雀头
            for (char tile : sortedTiles) {
                if (counts.get(tile) >= 2) {
                    candidates.add(repeatString(String.valueOf(tile), 2));
                }
            }
        } else {  // 查找顺子和刻子
            // 查找刻子
            for (char tile : sortedTiles) {
                if (counts.get(tile) >= 3) {
                    candidates.add(repeatString(String.valueOf(tile), 3));
                }
            }
            // 查找顺子
            for (int i = 0; i < sortedTiles.size() - 2; i++) {
                int curr = sortedTiles.get(i) - '0';
                int next = sortedTiles.get(i + 1) - '0';
                int last = sortedTiles.get(i + 2) - '0';
                if (curr + 2 == next + 1 && next + 1 == last) {
                    candidates.add("" + curr + next + last);
                }
            }
        }
        
        return candidates;
    }
    
    // 移除指定牌型
    private String removeTiles(String tiles, String pattern) {
        StringBuilder sb = new StringBuilder(tiles);
        for (char tile : pattern.toCharArray()) {
            int index = sb.indexOf(String.valueOf(tile));
            if (index != -1) {
                sb.deleteCharAt(index);
            }
        }
        return sb.toString();
    }
    
    // 判断是否能和牌
    private boolean canWin(String tiles) {
        if (tiles.isEmpty()) {
            return true;
        }
        
        for (String pattern : findCandidates(tiles, 2)) {
            if (canWin(removeTiles(tiles, pattern))) {
                return true;
            }
        }
        return false;
    }
    
    // 主求解函数
    public List<String> solve(String tiles) {
        Set<String> winningTiles = new TreeSet<>();  // 使用TreeSet自动排序去重
        tiles = tiles.replaceAll("\\s+", "");
        
        // 尝试每种可能的第14张牌
        for (String tile : findCandidates(tiles, 0)) {
            // 尝试每种可能的雀头
            for (String pair : findCandidates(tiles + tile, 1)) {
                String remaining = removeTiles(tiles + tile, pair);
                if (canWin(remaining)) {
                    winningTiles.add(tile);
                    break;
                }
            }
        }
        
        if (winningTiles.isEmpty()) {
            return Collections.singletonList("0");
        }
        return new ArrayList<>(winningTiles);
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String tiles = scanner.nextLine();
        
        MahjongSolver solver = new MahjongSolver();
        List<String> result = solver.solve(tiles);
        
        System.out.println(String.join(" ", result));
        scanner.close();
    }
}
class MahjongSolver:
    def __init__(self):
        self.NUMBERS = list(map(str, range(1, 10)))
    
    def find_candidates(self, tiles: str, mode: int) -> list:
        """查找不同模式下的可用牌组合"""
        # 统计每种牌的数量
        counts = {}
        for tile in tiles:
            counts[tile] = counts.get(tile, 0) + 1
        
        sorted_tiles = ''.join(sorted(counts.keys()))
        
        if mode == 0:  # 查找可添加的牌
            return [num for num in self.NUMBERS 
                   if num not in counts or counts[num] < 4]
        elif mode == 1:  # 查找雀头
            return [tile * 2 for tile in sorted_tiles 
                   if counts[tile] >= 2]
        else:  # 查找顺子和刻子
            melds = []
            # 查找刻子
            melds.extend(tile * 3 for tile in sorted_tiles 
                        if counts[tile] >= 3)
            # 查找顺子
            for i in range(len(sorted_tiles) - 2):
                if (int(sorted_tiles[i]) + 2 == 
                    int(sorted_tiles[i + 1]) + 1 == 
                    int(sorted_tiles[i + 2])):
                    melds.append(sorted_tiles[i:i + 3])
            return melds
    
    def remove_tiles(self, tiles: str, pattern: str) -> str:
        """移除指定牌型"""
        result = tiles
        for tile in pattern:
            result = result.replace(tile, '', 1)
        return result
    
    def can_win(self, tiles: str) -> bool:
        """判断是否能和牌"""
        if not tiles:
            return True
        
        # 尝试所有可能的顺子和刻子
        for pattern in self.find_candidates(tiles, 2):
            if self.can_win(self.remove_tiles(tiles, pattern)):
                return True
        return False
    
    def solve(self, tiles: str) -> list:
        """找出所有可能的和牌"""
        winning_tiles = []
        tiles = tiles.replace(' ', '')
        
        # 尝试每种可能的第14张牌
        for tile in self.find_candidates(tiles, 0):
            # 尝试每种可能的雀头
            for pair in self.find_candidates(tiles + tile, 1):
                remaining = self.remove_tiles(tiles + tile, pair)
                if self.can_win(remaining):
                    winning_tiles.append(tile)
                    break
        
        return winning_tiles if winning_tiles else ['0']

def main():
    solver = MahjongSolver()
    tiles = input()
    result = solver.solve(tiles)
    print(' '.join(result))

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:回溯搜索
  • 时间复杂度:,其中 是可能的牌型组合数
  • 空间复杂度:,用于递归栈深度