1. 顺子刻子
题目:小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:1.总共有36张牌,每张牌是1~9。每个数字4张牌。2.你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌(1)14张牌中有2张相同数字的牌,称为雀头。(2)除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)例如:1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。
#include <iostream>#include <vector>using namespace std;vector<int> card(9);//检查剩余的牌能否组成n个顺子或刻子bool hasTrible(int n){if(n==0) return true;for(int i=0; i<card.size(); ++i){//因为仍是从1开始查验,因此若检查到其牌数>=3,则必定是刻子if(card[i]>2){card[i] -= 3;if(hasTrible(n-1)){ //检查余下的牌能否组成n-1个顺子或刻子card[i] += 3;return true;}card[i] += 3;}//否则只能是顺子else if(i<card.size()-2 && card[i]>0 && card[i+1]>0 && card[i+2]>0){card[i]--;card[i+1]--;card[i+2]--;if(hasTrible(n-1)){card[i]++;card[i+1]++;card[i+2]++;return true;}card[i]++;card[i+1]++;card[i+2]++;}}return false;}//检查14张牌能否和牌bool isWin(){for(int i=0; i<9; ++i){ //依次把1~9作为雀头拿出来,检查剩下的12张牌能否顺或刻子if(card[i]<2)continue;card[i] -= 2;if(hasTrible(4)){card[i] += 2;return true;}card[i] += 2;}return false;}int main(){vector<int> res;int tmp;for(int i=0; i<13; ++i){cin >> tmp;card[tmp-1]++;}for(int i=0; i<9; ++i){ //1~9依次添加,检查是否可以和牌if(card[i]>3)continue;card[i]++;if(isWin()) //如果添加的这张牌可以和牌,则将其加入输出结果res.push_back(i+1);card[i]--;}if(res.empty()) res.push_back(0);for(int i=0; i<res.size(); ++i)cout << res[i] << ' ';return 0;}
2. 八皇后进阶
题目:设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在同一条对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
算法思想:
为了判断一个位置所在的列和两条斜线上是否已经有皇后,使用三个集合 columns、diagonals1和diagonals2分别记录每一列以及两个方向的每条斜线上是否有皇后。
列的表示法很直观,一共有 NN 列,每一列的下标范围从 00 到 N-1N−1,使用列的下标即可明确表示每一列。
如何表示两个方向的斜线呢?对于每个方向的斜线,需要找到斜线上的每个位置的行下标与列下标之间的关系。
方向一的斜线为从左上到右下方向,同一条斜线上的每个位置满足行下标与列下标之差相等,例如 (0,0)(0,0) 和 (3,3)(3,3) 在同一条方向一的斜线上。因此使用行下标与列下标之差即可明确表示每一条方向一的斜线。方向二同理。
class Solution {public:vector<vector<string>> solveNQueens(int n) {auto solutions = vector<vector<string>>();auto queens = vector<int>(n, -1);auto columns = unordered_set<int>();auto diagonals1 = unordered_set<int>();auto diagonals2 = unordered_set<int>();backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);return solutions;}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) {if (row == n) {vector<string> board = generateBoard(queens, n);solutions.push_back(board);} else {for (int i = 0; i < n; i++) {if (columns.find(i) != columns.end()) {continue;}int diagonal1 = row - i;if (diagonals1.find(diagonal1) != diagonals1.end()) {continue;}int diagonal2 = row + i;if (diagonals2.find(diagonal2) != diagonals2.end()) {continue;}queens[row] = i;columns.insert(i);diagonals1.insert(diagonal1);diagonals2.insert(diagonal2);backtrack(solutions, queens, n, row + 1, columns, diagonals1, diagonals2);queens[row] = -1;columns.erase(i);diagonals1.erase(diagonal1);diagonals2.erase(diagonal2);}}}vector<string> generateBoard(vector<int> &queens, int n) {auto board = vector<string>();for (int i = 0; i < n; i++) {string row = string(n, '.');row[queens[i]] = 'Q';board.push_back(row);}return board;}}My solution:(Better)class Solution {public:vector<string> buffer;string line;vector<int> pos;vector<int> Visit;int N;vector<vector<string> > res;vector<vector<string>> solveNQueens(int n) {if(n<=0) return res;N = n;pos.resize(n);Visit.resize(n);line = "";for (int i = 0; i < N; i++) {line += ".";Visit[i] = 0;}find_res(0);return res;}bool isValid(int i, int j) {for (int x = 0; x < i; x++) {if (abs(x - i) == abs(pos[x] - j)) //good, but time cost:O(n)return false;}return true;}void find_res(int index) {if (index == N) {res.push_back(buffer);return ;}for (int i = 0; i < N; i++) {if (Visit[i] || !isValid(index, i) ) continue;Visit[i] = 1;line[i] = 'Q';buffer.push_back(line); //good!line[i] = '.';pos[index] = i;find_res(index + 1);buffer.pop_back();Visit[i] = 0;}}};
3. 搜索——矩阵上的单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
[‘A’,’B’,’C’,’E’],
[‘S’,’F’,’C’,’S’],
[‘A’,’D’,’E’,’E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false
class Solution {public:bool check(vector<vector<char>>& board, vector<vector<int>>& visited, int i, int j, string& s, int k) {if (board[i][j] != s[k]) {return false;} else if (k == s.length() - 1) {return true;}visited[i][j] = true;vector<pair<int, int>> directions{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};bool result = false;for (const auto& dir: directions) {int newi = i + dir.first, newj = j + dir.second;if (newi >= 0 && newi < board.size() && newj >= 0 && newj < board[0].size()) {if (!visited[newi][newj]) {bool flag = check(board, visited, newi, newj, s, k + 1);if (flag) {result = true;break;}}}}visited[i][j] = false;return result;}bool exist(vector<vector<char>>& board, string word) {int h = board.size(), w = board[0].size();vector<vector<int>> visited(h, vector<int>(w));for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {bool flag = check(board, visited, i, j, word, 0);if (flag) {return true;}}}return false;}};# mysolution:class Solution {private:int n,m;public:bool exist(vector<vector<char>>& board, string word) {n=board.size();m=board[0].size();vector<vector<bool>> visited(n,vector<bool>(m,false) );for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(backtrace(i,j,word,0,board,visited)){return true;}}}return false;}bool backtrace(int i,int j,string word,int k,vector<vector<char>> board,vector<vector<bool>> &visited) {std::cout<< "[" <<i <<","<<j <<"]" << ","<< k <<endl;if(board[i][j] != word[k]) return false;else if(k == word.length()-1) return true;bool flag = false;if(j>=1 && !visited[i][j-1]){visited[i][j] = true;flag = backtrace(i,j-1,word,k+1,board,visited);visited[i][j] = false;if(flag) return true;}if(j<m-1 && !visited[i][j+1]){visited[i][j] = true;flag = backtrace(i,j+1,word,k+1,board,visited);visited[i][j] = false;if(flag) return true;}if(i>=1 && !visited[i-1][j]){visited[i][j] = true;flag = backtrace(i-1,j,word,k+1,board,visited);visited[i][j] = false;if(flag) return true;}if(i<n-1 && !visited[i+1][j]){visited[i][j] = true;flag = backtrace(i+1,j,word,k+1,board,visited);visited[i][j] = false;if(flag) return true;}return false;}};
4. 无重复子集
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
算法核心:
**
- 排序
- 同层不重复判断.
- 每次循环起始位置为start…n-1而不是0…n-1
```cpp
class Solution {
private:
vector
> result; vector path; //注意:此处的use用来规范同层的行为,而不是子孙的行为。 void backtracking(vector & nums, int startIndex, vector & used) {
}result.push_back(path);for (int i = startIndex; i < nums.size(); i++) {// used[i - 1] == true,说明同一树支candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过// 而我们要对同一树层使用过的元素进行跳过if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {continue;}path.push_back(nums[i]);used[i] = true;backtracking(nums, i + 1, used);used[i] = false;path.pop_back();}
public:
vector
去重改进1:不用used数组,使用set
{
…
unordered_set
<a name="RMVuP"></a># 5. 分割——[分割回文串](https://leetcode-cn.com/problems/palindrome-partitioning/)给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。<br />返回 s 所有可能的分割方案。<br />**示例:**<br />**输入:** "aab"<br />**输出:**<br />[<br /> ["aa","b"],<br /> ["a","a","b"]<br />]```cppclass Solution {public:vector<vector<string>> partition(string s) {vector<vector<string>> res;vector<string> tmp;backtrace(0,s,tmp,res);return res;}void backtrace(int i, string s, vector<string> &tmp, vector<vector<string>> &res){int n = s.length();if(i==n){res.push_back(tmp);}for(int j=i+1;j<=n;j++){string xs = s.substr(i,j-i);if(is_symmetry(xs)){tmp.push_back(xs);backtrace(j,s,tmp,res);tmp.pop_back();}}}bool is_symmetry(string s){int n= s.length();//std::cout<<s;for(int i=0;i<n/2;i++){if(s[i] != s[n-i-1]){//std::cout<< "false" <<endl;return false;}}//std::cout<< "true" <<endl;return true;}};//优化:使用动态规划提前计算回文串://dp[i][j] = true when a[i] == a[j] && (j-i<=2 || dp[i+1][j-1]==true)
