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 />]
```cpp
class 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)