原始题目:雀魂启动
题目的大致意思就是,有13张排,加一张,就能胡,用贵州话讲就是一对将,然后加四坎牌。
一坎牌可以是三个一样的碰,也可以是顺子。现在,让你用程序去判定任意一组13排是否能胡,并给出需要哪个牌来胡牌。
这个显然是一个经典的回溯问题,但是和我们常见的回溯问题又有一些区别,比如:组合问题
class Solution {
public:
vector<vector<int>> arrs;
map<string,int> maps;
vector<vector<int>> subsets(vector<int>& nums) {
vector<int> path;
sort(nums.begin(),nums.end());
help(nums, path,0);
return arrs;
}
void help(vector<int>& nums,vector<int>& path,int begin){
sort(path.begin(),path.end());
stringstream ss;
for (auto k: path){
ss<<k<<",";
}
if(maps[ss.str()] == 0){
arrs.push_back(path);
maps[ss.str()]++;
}
if(path.size()==nums.size()){
return;
}
for(int i =begin;i<nums.size();i++){
if(find(path.begin(),path.end(),nums[i]) == path.end()){
path.push_back(nums[i]);
help(nums,path,i);
path.pop_back();
}
}
}
};
差别在哪呢?胡牌问题里面侧重一个胡字,对于哪个牌能胡,只是一个简单的暴力for循环就行,换句话说:
for(int i =1;i<=9;i++){
if(胡牌(i)){
cout<<i<<" ";
}
}
你就能完成这道题。
但是,这个胡牌,不是一个简单的事情,它也是回溯,但是细节和常规的组合问题不一样
组合问题:我希望找到一条路径,使得这条路径满足某种规则。
胡牌能不能当成寻路问题?
答案是不能简单的当成,但本身也是一个寻路过程,因为直接通过一条路[字符串]径直接判断能否胡牌是很困难的事情,但是组合问题本身只是期待获取这条路径。但是两者本身却都基于回溯进行操作。胡牌问题的难点在于怎么确定胡牌,使用回溯是一个好方法,先确定第一坎牌,然后确定第二坎牌,第三…,突然发现无法完成第n坎牌,回溯,第一次试探时,尝试的是碰!操作,那第二次试探时,则采用顺子操作,当顺子能成功时,则下面又可以碰,或者是顺子
组合问题:
可以看到,前面的胡牌问题已经变成一个二叉树,而后面的组合问题则是一个多叉树,不管是多插还是二叉,使用回溯都会很简单。而编程二叉树的胡牌问题在使用回溯时,可以直接通过后序遍历确定是否达成条件
//二叉树的后序
bool help(Node* root){
//op
bool f1 = help(root.left);
bool f2 = help(root.right);
if(f1){
return f1;
}
if(f2){
return f2;
}
}
源代码:
#include
#include
#include
using namespace std;
bool isSuccess(int n,map<int,int>& maps,vector<int>& path);
int main(){
map<int,int> maps;
vector<int> path;
int* arr = new int[13];
for(int i =0;i<13;i++){
cin>>arr[i];
maps[arr[i]]++;
}
for (int i =1;i<=9;i++){
if (maps[i]<4){
map<int,int> m2 = maps;
m2[i]++;
if(isSuccess(0,m2,path) == true){
cout<<i<<" ";
}
}
}
return 0;
}
bool isSuccess(int n,map<int,int>& maps,vector<int>& path){
if(n == 4){
for (auto k: maps){
if(k.second == 2){
return true;
}
}
return false;
}
for(int i =0;i<9;i++){
int pos = i+1;
if(maps[pos]>=3){
maps[pos] -= 3;
path.push_back(pos);
bool reback = isSuccess(n + 1,maps,path);
pos = path[path.size() - 1];
maps[pos] += 3;
path.pop_back();
if(reback){
return true;
}
}
if(maps[pos]>0&&maps[pos+1]>0&&maps[pos+2]>0){
maps[pos] -= 1;
maps[pos+1]-=1;
maps[pos+2]-= 1;
path.push_back(pos);
bool reback = isSuccess(n+1, maps,path);
pos = path[path.size() - 1];
path.pop_back();
maps[pos] += 1;
maps[pos+1] +=1;
maps[pos+2] +=1;
if(reback){
return true;
}
}
}
return false;
}
关于碰的操作:for循环,如果发现存在key>3的情况,则碰
关于顺子的操作,for循环,如果发现连续3个value>0的情况则顺子
觉得有人要吐槽:这特么算二叉树我倒立洗头
哎,我也是瞎讲的,从抽象层面上,确实就是一个二叉树,图都出来了