解题思路
这是一个回溯搜索问题。需要枚举所有可能的第14张牌,并判断是否能够和牌。
关键点:
- 枚举所有可能的第14张牌
- 判断是否能组成雀头
- 判断剩余牌是否能组成顺子或刻子
- 结果需要排序并去重
算法步骤:
- 统计当前手牌数量
- 尝试添加每种可能的牌
- 寻找可能的雀头
- 递归判断剩余牌是否能和牌
代码
#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()
算法及复杂度
- 算法:回溯搜索
- 时间复杂度:,其中 是可能的牌型组合数
- 空间复杂度:,用于递归栈深度