1. 顺子刻子

  1. 题目:小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。
  2. 于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:
  3. 1.总共有36张牌,每张牌是1~9。每个数字4张牌。
  4. 2.你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌
  5. 114张牌中有2张相同数字的牌,称为雀头。
  6. 2)除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777
  7. 例如:
  8. 1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,74个刻子和9的雀头,可以和牌
  9. 1 1 1 1 2 2 3 3 5 6 7 7 8 9 1做雀头,组123,123,567,789的四个顺子,可以和牌
  10. 1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。
  11. 现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. vector<int> card(9);
  5. //检查剩余的牌能否组成n个顺子或刻子
  6. bool hasTrible(int n){
  7. if(n==0) return true;
  8. for(int i=0; i<card.size(); ++i){
  9. //因为仍是从1开始查验,因此若检查到其牌数>=3,则必定是刻子
  10. if(card[i]>2){
  11. card[i] -= 3;
  12. if(hasTrible(n-1)){ //检查余下的牌能否组成n-1个顺子或刻子
  13. card[i] += 3;
  14. return true;
  15. }
  16. card[i] += 3;
  17. }
  18. //否则只能是顺子
  19. else if(i<card.size()-2 && card[i]>0 && card[i+1]>0 && card[i+2]>0){
  20. card[i]--;
  21. card[i+1]--;
  22. card[i+2]--;
  23. if(hasTrible(n-1)){
  24. card[i]++;
  25. card[i+1]++;
  26. card[i+2]++;
  27. return true;
  28. }
  29. card[i]++;
  30. card[i+1]++;
  31. card[i+2]++;
  32. }
  33. }
  34. return false;
  35. }
  36. //检查14张牌能否和牌
  37. bool isWin(){
  38. for(int i=0; i<9; ++i){ //依次把1~9作为雀头拿出来,检查剩下的12张牌能否顺或刻子
  39. if(card[i]<2)
  40. continue;
  41. card[i] -= 2;
  42. if(hasTrible(4)){
  43. card[i] += 2;
  44. return true;
  45. }
  46. card[i] += 2;
  47. }
  48. return false;
  49. }
  50. int main(){
  51. vector<int> res;
  52. int tmp;
  53. for(int i=0; i<13; ++i){
  54. cin >> tmp;
  55. card[tmp-1]++;
  56. }
  57. for(int i=0; i<9; ++i){ //1~9依次添加,检查是否可以和牌
  58. if(card[i]>3)
  59. continue;
  60. card[i]++;
  61. if(isWin()) //如果添加的这张牌可以和牌,则将其加入输出结果
  62. res.push_back(i+1);
  63. card[i]--;
  64. }
  65. if(res.empty()) res.push_back(0);
  66. for(int i=0; i<res.size(); ++i)
  67. cout << res[i] << ' ';
  68. return 0;
  69. }

2. 八皇后进阶

  1. 题目:
  2. 设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,
  3. 其中每个皇后都不同行、不同列,也不在同一条对角线上。
  4. 这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。

算法思想:
为了判断一个位置所在的列和两条斜线上是否已经有皇后,使用三个集合 columnsdiagonals1diagonals2分别记录每一列以及两个方向的每条斜线上是否有皇后

列的表示法很直观,一共有 NN 列,每一列的下标范围从 00 到 N-1N−1,使用列的下标即可明确表示每一列。

如何表示两个方向的斜线呢?对于每个方向的斜线,需要找到斜线上的每个位置的行下标与列下标之间的关系。
方向一的斜线为从左上到右下方向,同一条斜线上的每个位置满足行下标与列下标之差相等,例如 (0,0)(0,0) 和 (3,3)(3,3) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。方向二同理。
(BHWILO5SFGTJ9VO)ZUKJWY.png

  1. class Solution {
  2. public:
  3. vector<vector<string>> solveNQueens(int n) {
  4. auto solutions = vector<vector<string>>();
  5. auto queens = vector<int>(n, -1);
  6. auto columns = unordered_set<int>();
  7. auto diagonals1 = unordered_set<int>();
  8. auto diagonals2 = unordered_set<int>();
  9. backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
  10. return solutions;
  11. }
  12. void backtrack(vector<vector<string>> &solutions, vector<int> &queens, int n, int row, unordered_set<int> &columns, unordered_set<int> &diagonals1, unordered_set<int> &diagonals2) {
  13. if (row == n) {
  14. vector<string> board = generateBoard(queens, n);
  15. solutions.push_back(board);
  16. } else {
  17. for (int i = 0; i < n; i++) {
  18. if (columns.find(i) != columns.end()) {
  19. continue;
  20. }
  21. int diagonal1 = row - i;
  22. if (diagonals1.find(diagonal1) != diagonals1.end()) {
  23. continue;
  24. }
  25. int diagonal2 = row + i;
  26. if (diagonals2.find(diagonal2) != diagonals2.end()) {
  27. continue;
  28. }
  29. queens[row] = i;
  30. columns.insert(i);
  31. diagonals1.insert(diagonal1);
  32. diagonals2.insert(diagonal2);
  33. backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);
  34. queens[row] = -1;
  35. columns.erase(i);
  36. diagonals1.erase(diagonal1);
  37. diagonals2.erase(diagonal2);
  38. }
  39. }
  40. }
  41. vector<string> generateBoard(vector<int> &queens, int n) {
  42. auto board = vector<string>();
  43. for (int i = 0; i < n; i++) {
  44. string row = string(n, '.');
  45. row[queens[i]] = 'Q';
  46. board.push_back(row);
  47. }
  48. return board;
  49. }
  50. }
  51. My solution:(Better)
  52. class Solution {
  53. public:
  54. vector<string> buffer;
  55. string line;
  56. vector<int> pos;
  57. vector<int> Visit;
  58. int N;
  59. vector<vector<string> > res;
  60. vector<vector<string>> solveNQueens(int n) {
  61. if(n<=0) return res;
  62. N = n;
  63. pos.resize(n);
  64. Visit.resize(n);
  65. line = "";
  66. for (int i = 0; i < N; i++) {
  67. line += ".";
  68. Visit[i] = 0;
  69. }
  70. find_res(0);
  71. return res;
  72. }
  73. bool isValid(int i, int j) {
  74. for (int x = 0; x < i; x++) {
  75. if (abs(x - i) == abs(pos[x] - j)) //good, but time cost:O(n)
  76. return false;
  77. }
  78. return true;
  79. }
  80. void find_res(int index) {
  81. if (index == N) {
  82. res.push_back(buffer);
  83. return ;
  84. }
  85. for (int i = 0; i < N; i++) {
  86. if (Visit[i] || !isValid(index, i) ) continue;
  87. Visit[i] = 1;
  88. line[i] = 'Q';
  89. buffer.push_back(line); //good!
  90. line[i] = '.';
  91. pos[index] = i;
  92. find_res(index + 1);
  93. buffer.pop_back();
  94. Visit[i] = 0;
  95. }
  96. }
  97. };

3. 搜索——矩阵上的单词搜索

给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:
board =
[
[‘A’,’B’,’C’,’E’],
[‘S’,’F’,’C’,’S’],
[‘A’,’D’,’E’,’E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false

  1. class Solution {
  2. public:
  3. bool check(vector<vector<char>>& board, vector<vector<int>>& visited, int i, int j, string& s, int k) {
  4. if (board[i][j] != s[k]) {
  5. return false;
  6. } else if (k == s.length() - 1) {
  7. return true;
  8. }
  9. visited[i][j] = true;
  10. vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
  11. bool result = false;
  12. for (const auto& dir: directions) {
  13. int newi = i + dir.first, newj = j + dir.second;
  14. if (newi >= 0 && newi < board.size() && newj >= 0 && newj < board[0].size()) {
  15. if (!visited[newi][newj]) {
  16. bool flag = check(board, visited, newi, newj, s, k + 1);
  17. if (flag) {
  18. result = true;
  19. break;
  20. }
  21. }
  22. }
  23. }
  24. visited[i][j] = false;
  25. return result;
  26. }
  27. bool exist(vector<vector<char>>& board, string word) {
  28. int h = board.size(), w = board[0].size();
  29. vector<vector<int>> visited(h, vector<int>(w));
  30. for (int i = 0; i < h; i++) {
  31. for (int j = 0; j < w; j++) {
  32. bool flag = check(board, visited, i, j, word, 0);
  33. if (flag) {
  34. return true;
  35. }
  36. }
  37. }
  38. return false;
  39. }
  40. };
  41. # mysolution:
  42. class Solution {
  43. private:
  44. int n,m;
  45. public:
  46. bool exist(vector<vector<char>>& board, string word) {
  47. n=board.size();
  48. m=board[0].size();
  49. vector<vector<bool>> visited(n,vector<bool>(m,false) );
  50. for(int i=0;i<n;i++){
  51. for(int j=0;j<m;j++){
  52. if(backtrace(i,j,word,0,board,visited)){
  53. return true;
  54. }
  55. }
  56. }
  57. return false;
  58. }
  59. bool backtrace(int i,int j,string word,int k,vector<vector<char>> board,vector<vector<bool>> &visited) {
  60. std::cout<< "[" <<i <<","<<j <<"]" << ","<< k <<endl;
  61. if(board[i][j] != word[k]) return false;
  62. else if(k == word.length()-1) return true;
  63. bool flag = false;
  64. if(j>=1 && !visited[i][j-1]){
  65. visited[i][j] = true;
  66. flag = backtrace(i,j-1,word,k+1,board,visited);
  67. visited[i][j] = false;
  68. if(flag) return true;
  69. }
  70. if(j<m-1 && !visited[i][j+1]){
  71. visited[i][j] = true;
  72. flag = backtrace(i,j+1,word,k+1,board,visited);
  73. visited[i][j] = false;
  74. if(flag) return true;
  75. }
  76. if(i>=1 && !visited[i-1][j]){
  77. visited[i][j] = true;
  78. flag = backtrace(i-1,j,word,k+1,board,visited);
  79. visited[i][j] = false;
  80. if(flag) return true;
  81. }
  82. if(i<n-1 && !visited[i+1][j]){
  83. visited[i][j] = true;
  84. flag = backtrace(i+1,j,word,k+1,board,visited);
  85. visited[i][j] = false;
  86. if(flag) return true;
  87. }
  88. return false;
  89. }
  90. };

4. 无重复子集

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

算法核心:
1RTAA8%JFZSVE~(C7C}]H%3.png**

  1. 排序
  2. 同层不重复判断.
  3. 每次循环起始位置为start…n-1而不是0…n-1 ```cpp class Solution { private: vector> result; vector path; //注意:此处的use用来规范同层的行为,而不是子孙的行为。 void backtracking(vector& nums, int startIndex, vector& used) {
    1. result.push_back(path);
    2. for (int i = startIndex; i < nums.size(); i++) {
    3. // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
    4. // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
    5. // 而我们要对同一树层使用过的元素进行跳过
    6. if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
    7. continue;
    8. }
    9. path.push_back(nums[i]);
    10. used[i] = true;
    11. backtracking(nums, i + 1, used);
    12. used[i] = false;
    13. path.pop_back();
    14. }
    }

public: vector> subsetsWithDup(vector& nums) { result.clear(); path.clear(); vector used(nums.size(), false); sort(nums.begin(), nums.end()); // 去重需要排序 backtracking(nums, 0, used); return result; } };

去重改进1:不用used数组,使用set { … unordered_set uset; for (int i = startIndex; i < nums.size(); i++) { if (uset.find(nums[i]) != uset.end()) { continue; } uset.insert(nums[i]); path.push_back(nums[i]); backtracking(nums, i + 1); path.pop_back(); } … } 去重改进2:不用used数组,也不用set if (i > startIndex && nums[i] == nums[i - 1] ) { continue; }

  1. <a name="RMVuP"></a>
  2. # 5. 分割——[分割回文串](https://leetcode-cn.com/problems/palindrome-partitioning/)
  3. 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。<br />返回 s 所有可能的分割方案。<br />**示例:**<br />**输入:** "aab"<br />**输出:**<br />[<br /> ["aa","b"],<br /> ["a","a","b"]<br />]
  4. ```cpp
  5. class Solution {
  6. public:
  7. vector<vector<string>> partition(string s) {
  8. vector<vector<string>> res;
  9. vector<string> tmp;
  10. backtrace(0,s,tmp,res);
  11. return res;
  12. }
  13. void backtrace(int i, string s, vector<string> &tmp, vector<vector<string>> &res){
  14. int n = s.length();
  15. if(i==n){
  16. res.push_back(tmp);
  17. }
  18. for(int j=i+1;j<=n;j++){
  19. string xs = s.substr(i,j-i);
  20. if(is_symmetry(xs)){
  21. tmp.push_back(xs);
  22. backtrace(j,s,tmp,res);
  23. tmp.pop_back();
  24. }
  25. }
  26. }
  27. bool is_symmetry(string s){
  28. int n= s.length();
  29. //std::cout<<s;
  30. for(int i=0;i<n/2;i++){
  31. if(s[i] != s[n-i-1]){
  32. //std::cout<< "false" <<endl;
  33. return false;
  34. }
  35. }
  36. //std::cout<< "true" <<endl;
  37. return true;
  38. }
  39. };
  40. //优化:使用动态规划提前计算回文串:
  41. //dp[i][j] = true when a[i] == a[j] && (j-i<=2 || dp[i+1][j-1]==true)