// 环境: centos7.2, g++ v4.8.5#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>using namespace std;enum CombineType
{CombineType_Null = 0, // 单牌CombineType_Dui = 1, // 对子CombineType_Ke = 2, // 刻CombineType_Shun = 3 // 顺子
};struct Combine // 一种组合
{CombineType type;vector<int> cards;Combine(){type = CombineType_Null;}
};map<int, vector<int>> classify(vector<int>& v)
{map<int, vector<int>> m;for(auto& it : v){if(m.find(it) == m.end()){m[it] = {};}m[it].push_back(it);}#if 1for(auto& it : m){printf("%d: ", it.first);for(auto& it2 : it.second)printf("%d ", it2);printf("\n");}
#endifreturn m;
}// 从m中找一种符合组合类型为type的组合, 这个组合里最小的卡id为min
Combine findCombine(map<int, vector<int>>& m, int min, CombineType type)
{printf("开始查找: min=%d, type=%d\n", min, type);Combine comb;if(type == CombineType_Dui || type == CombineType_Ke) // 查找对子或刻{uint n = type == CombineType_Dui ? 2 : 3;if(m[min].size() >= n) // 能找到对子或刻{for(int i = 0; i < n; i++) // 从m[min]中取n张卡到comb.cards中{comb.cards.push_back(min);m[min].pop_back();if(m[min].empty())m.erase(min);}comb.type = type;}}else if(type == CombineType_Shun && min <= 7) // 8往后不可能组成顺子了{if(m.find(min) != m.end() && m.find(min+1) != m.end() && m.find(min+2) != m.end()) // 找到以min开始的顺子{comb.cards = {min, min+1, min+2};comb.type = type;for(auto& it : comb.cards){m[it].pop_back();if(m[it].empty())m.erase(it);}}}return comb;
}void display(Combine comb)
{printf("comb.type = %d, cards = ", comb.type);for(auto& it : comb.cards)printf("%d ", it);printf("\n\n");
}void displayCombines(vector<Combine>& v)
{for(auto& it : v){for(auto& it2 : it.cards){printf(" %d", it2);}printf(",");}printf("\n\n");
}// vector<Combine>: 一种组合方案
// vector<vector<Combine>>: 多种组合方案
vector<vector<Combine>> getCombines(vector<int>& v)
{vector<vector<Combine>> ret;auto m = classify(v);auto firstCard = [&m]() // 查找m中键最小的那个数{for(auto& it : m){return it.first;}return 0;};auto useCombine = [&m](Combine& comb) // 使用组合{for(auto& it : comb.cards) // 从m中删除comb中的卡牌{m[it].pop_back();if(m[it].empty())m.erase(it);}};auto recyle = [&m](Combine& comb) // 回收comb中的卡牌{for(auto& it : comb.cards){if(m.find(it) == m.end()){m[it] = {};}m[it].push_back(it);}};printf("\n开始查找新的组合方案-----------------\n");vector<Combine> stack;int min = firstCard();CombineType oldType = CombineType_Null; // 下次从此类型之后开始找do{bool b = false;auto types = {CombineType_Dui, CombineType_Ke, CombineType_Shun};for(auto& it : types){if(it <= oldType) // 以前找过的组合类型, 不再继续寻找continue;auto comb = findCombine(m, min, it);if(comb.type == it) // 找到一个组合{stack.push_back(comb);b = true;break;}}if(b) {printf("找到一个组合: ");display(stack.back());if(m.empty()) // 找到一种组合方案(牌分配完了){ret.push_back(stack);printf("【找到一种组合方案】:");displayCombines(stack);}else{auto tmp = min;min = firstCard(); // 确定下次要找的牌if(tmp != min)oldType = CombineType_Null; // 下次查找的类型重置}}else if(!stack.empty()) // 有可回收的组合(回归查找准备) {auto comb = stack.back(); // 取(复制)栈顶元素oldType = comb.type; // 记录上次此组合开始查找的类型min = comb.cards[0]; // 下次从此牌开始找printf("回收一个组合: ");display(comb);recyle(comb); // 回收栈顶元素中的卡牌stack.pop_back(); // 弹出栈顶组合}else // 没有可回收的组合(即方案寻找结束)break;}while(true);return ret;
}int main()
{//vector<int> v = {1, 1, 1, 2, 2, 2, 8, 8};vector<int> v = {1, 1, 2, 4, 5, 6};auto vvc = getCombines(v);printf("--------------------------------\n");printf("vvc.size=%d\n", vvc.size());for(auto& vc : vvc){for(auto& comb : vc){printf("%d: ", comb.type);for(auto& c : comb.cards)printf("%d ", c);printf("\n");}printf("\n");}return 0;
}
以上代码是模拟麻将的序数牌,如万、筒、条,将同是万字的牌按对子、刻、顺子去组合, 列出所有可能的组合!