記錄整理回溯算法專題
1. 八皇后
問題描述:
在一個8*8的矩形格子中排放8個皇后,要滿足的條件包括: 任意兩個皇后都不能在同一行/列/對角線(斜率等於1/-1). 要求編程給出所有第一行第一列有皇后的解 (注:解的輸出用8個數字表示,如:基本解{1,5,8,6,3,7,2,4},其中’1’表示第一行第一列即(1,1)處有皇后, 後同)思路:
跟窮舉查不多,我們依次在每一行放一個皇后,而一行有八列,所以我們在放置一個皇后時,就要馬上判斷皇后放置的列是否符合條件,如果符合,我們就放置下一個皇后,如果不符合,我們就嘗試着將皇后放置在下一列,接着像剛纔一樣判斷皇后放置的列是否合法…如此循環,直到最後一列爲止。
#include <iostream>
#include <stdio.h>
#include <math.h>
#define N 8
int queen[N] = {0}, sum = 0; // 數組queen[N]的下標表示皇后所在的行,queen[N]其值表示皇后所在的列
/**
* 打印八皇后圖表形式
*/
void Paint() {
for (int i = 0; i < N; i++) {
for (int j=0; j < N; j++) {
if(queen[i] == j)
printf("■ "); // 輸出皇后
else
printf("□ ");
}
printf("\n");
}
printf("\n");
}
/**
* 輸出所有皇后座標
*/
void PrintfPutQueen() {
for (int i = 0; i < N; i++) {
printf("(%d,%d) ", i, queen[i]);
}
printf("\n");
}
int CanPlace(int row) {
for (int j = 0; j < row; j++) { // 將第 row 行皇后與前面所有行的皇后進行比較
// 檢查橫排和對角線上是否可以放置皇后
if(queen[row] == queen[j] || abs(queen[j]-queen[row]) == (row-j) )
return 0; // 不能放置皇后
}
return 1; // 可以放置皇后
}
/**
* 放置皇后
*/
void PutQueen(int row) {
if (row >= N) { //皇后已經全部放完時
Paint();
sum++;
// return;
} else {
for (int i = 0;i < N; i++) { // 總共有N列,一列一列的試探放置皇后
queen[row] = i; // 將第row行皇后放在第 i 列上面
if (CanPlace(row)) { // 判斷是否可以放置該皇后
PutQueen(row + 1); // 放下一個行皇后
}
}
}
}
int main() {
PutQueen(0); // 從橫座標爲0開始依次嘗試
printf("\nSum = %d\n", sum);
return 0;
}
不使用全局變量方式:
#include <stdio.h>
#include <stdlib.h>
#define N 8
/**
* 打印八皇后圖表形式
*/
void Paint(int *queen) {
for (int i = 0; i < N; i++) {
for (int j=0; j < N; j++) {
if(queen[i] == j)
printf("■ "); // 輸出皇后
else
printf("□ ");
}
printf("\n");
}
printf("\n");
}
/**
* 輸出所有皇后座標
*/
void PrintfPutQueen(int *queen) {
for (int i = 0; i < N; i++) {
printf("(%d,%d) ", i, queen[i]);
}
printf("\n");
}
int CanPut(int row, int *queen) {
for (int i = 0; i < row; i++) { // 將第 row 行皇后與前面所有行的皇后進行比較
if (queen[row] == queen[i] || (abs(queen[row] - queen[i]) == (row - i))) // 檢查橫排和對角線上是否可以放置皇后
return 0; // 不能放置皇后
}
return 1; // 可以放置皇后
}
/**
* 放置皇后
*/
int PutQueen(int row, int *queen) {
int sum = 0;
if (row >= N) { // 說明皇后都已經放好了
Paint(queen);
return 1;
} else {
for (int i = 0; i < N; i++) { // 總共有N列,一列一列的試探放置皇后
queen[row] = i; // 將第row行皇后放置在第i列
if (CanPut(row, queen)) { // 判斷是否可以放置該皇后
sum += PutQueen(row + 1, queen); // 放下一個行皇后
}
}
}
return sum;
}
int main() {
// 數組queen[N]的下標表示皇后所在的行,queen[N]其值表示皇后所在的列
int queen[N] = {0}, sum = 0;
sum = PutQueen(0, queen);
printf("Sum = %d\n", sum);
return 0;
}
2. 2n皇后
問題描述:
給定一個n*n的棋盤,棋盤中有一些位置不能放皇后。
現在要向棋盤中放入n個黑皇后和n個白皇后,使任意的兩個黑皇后都不在同一行、同一列或同一條對角線上,任意的兩個白皇后都不在同一行、同一列或同一條對角線上。問總共有多少种放法?n小於等於8。思路:
其實就是在n皇后基礎上改進的,按照n皇后思想,先放黑的,黑的都放好後再放白的,就是要多加一個
判斷已經放了黑的的位置就不能再放白的了。輸入格式
輸入的第一行爲一個整數n,表示棋盤的大小。
接下來n行,每行n個0或1的整數
(如果一個整數爲1,表示對應的位置可以放皇后,如果一個整數爲0,表示對應的位置不可以放皇后)輸出格式
輸出一個整數,表示總共有多少种放法。樣例輸入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
樣例輸出
2樣例輸入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
樣例輸出
0
/**
* Creation : 2019.04.04 14:50
* Last Reversion : 2019.03.04 15:22
* Author : Lingyong Smile {[email protected]}
* File Type : cpp
* -----------------------------------------------------------------
* EightQueen(2n皇后問題)
* -----------------------------------------------------------------
* Crop right @ 2019 Lingyong Smile {[email protected]}
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int white[10], black[10], a[10][10], sum = 0, N = 0;
int CanPlaceBlack(int row) {
for (int i = 0; i < row; i++) {
if (black[row] == black[i] || (abs(black[row] - black[i]) == (row - i)))
return 0;
}
return 1;
}
int CanPlaceWhite(int row) {
for (int i = 0; i < row; i++) {
if (white[row] == white[i] || (abs(white[row] - white[i]) == (row - i)))
return 0;
}
return 1;
}
void PutWhite(int row) {
if (row >= N) {
sum++;
} else {
for (int i = 0; i < N; i++) {
white[row] = i;
if (a[row][i] == 0 || black[row] == white[row]) // 該位置不能放或者已經放了黑的
continue;
if (CanPlaceWhite(row)) {
PutWhite(row + 1);
}
}
}
}
void PutBlack(int row) {
if (row >= N) {
PutWhite(0);
} else {
for (int i = 0; i < N; i++) {
black[row] = i;
if (a[row][i] == 0)
continue;
if (CanPlaceBlack(row)) {
PutBlack(row + 1);
}
}
}
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%d", &a[i][j]);
}
}
PutBlack(0);
printf("Sum = %d\n", sum);
return 0;
}
3. 國際象棋
題目描述:
假設國際象棋盤有5*5共有25個格子。設計一個程序,使棋子從初試位置(棋盤格編號爲1的位置)開始跳馬,能夠把棋盤的格子全部走一遍,每個格子只允許走一次,要求:
(1)輸出一個解(用二維數組來記錄馬跳的過程,即【步號,棋盤格編號】,左上角爲第1步起點)
(2)求總共有多少解?
棋盤格編號爲:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25思想:
回溯思想 (和八皇后一個思路,都是要走完整個棋盤,即遞歸出口條件是類似的)
#include <stdio.h>
int a[5][5]; // 棋盤
int b[5][5] = {0}; // 用於標記棋盤對應位置是否已走過
int step[25] = {0}, step_num = 0, sum = 0; // 用於記錄每步走過的位置,sum爲總共有多少總跳法
7
void Init(int a[][5]) { // 初始化棋盤
int k = 1;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
a[i][j] = k++;
}
}
}
/**
* 判斷該跳是否可行
*/
int Check(int x, int y) {
if (x < 0 || x >= 5 || y < 0 || y >= 5 || b[x][y] == 1) // 不要忘了判斷該位置是否已走過
return 0;
else
return 1;
}
/**
* 探尋可走的路
*/
void Search(int x, int y) {
if (Check(x, y)) { // 如果該跳可行
step[step_num++] = a[x][y]; // step下標從1開始
if (step_num == 25) { // 若25個均跳好了
sum++;
printf("\nThe %d Jump:\n", sum);
for (int i = 0; i < step_num; i++) {
printf("[%d,%d]\n", i + 1, step[i]);
}
}
b[x][y] = 1; // 標記該位置已經走過了
Search(x + 1, y + 2); // 遞歸調用,尋找下一跳
Search(x + 1, y - 2);
Search(x - 1, y + 2);
Search(x - 1, y - 2);
Search(x + 2, y + 1);
Search(x + 2, y - 1);
Search(x - 2, y + 1);
Search(x - 2, y - 1);
step_num--; // 若尋找的下一跳都不可行,取消這一步,回退到上一步
b[x][y] = 0; // 把這步設置標記未走過
}
}
int main() {
Init(a); // 初始化棋盤
Search(0, 0);
printf("\nSum = %d", sum);
return 0;
}
4. 馬跳日
題目描述:
在半個中國象棋棋盤上,馬在左下角(1, 1)處, 馬走日字,而且只能往右走,
不能向左,可上可下,求從起點到(m, n)處有幾種不同的走法(函數的遞歸調用)
其中,1<= m <=5, 1<= n <=9
#include <stdio.h>
int horse(int x1, int y1, int x2, int y2) {
int sum = 0;
if (y1 > y2 || (y1 == y2 && x1 != x2)) // 起點和終點不能在同一列
return 0;
else if (x1 == x2 && y1 == y2) // 走到了終點,返回1,然後遞歸回去
return 1;
else {
if (x1 - 1 >= 1 && y1 + 2 <= 9) sum += horse(x1 - 1, y1 + 2, x2, y2);
if (x1 - 2 >= 1 && y1 + 1 <= 9) sum += horse(x1 - 2, y1 + 1, x2, y2);
if (x1 + 2 <= 5 && y1 + 1 <= 9) sum += horse(x1 + 2, y1 + 1, x2, y2);
if (x1 + 1 <= 5 && y1 + 2 <= 9) sum += horse(x1 + 1, y1 + 2, x2, y2);
return sum;
}
}
int main() {
int m, n;
printf("請輸入目的地址:\n");
scanf("%d %d", &m, &n);
printf("目的結果數爲%d\n", horse(1, 1, m, n));
return 0;
}
5. 騎士最短路徑
問題描述:
在一個8*8的矩形格子中找起始位置 start(x, y) 到 終點位置 end(x, y) 的最短路徑。
輸出最短的路徑長度,並輸出路徑順序點(這部分沒有完成)
思路:
使用BFS思想(類似於層次遍歷),單源最短路徑。
#include <iostream>
using namespace std;
#include <queue>
typedef struct Point {
int x;
int y;
} Point;
// 定義馬日跳
int direction[][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1},
{1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
int BFS(Point start, Point end) {
int step[8][8] = {0}; // 存放起始點到達8x8網格每一點時刻走的步數
Point cur, next;
queue<Point> que;
que.push(start); // 起始點入隊
while (!que.empty()) { // 當隊列不爲空
cur = que.front(); // 隊首元素出隊
que.pop();
if (cur.x == end.x && cur.y == end.y) {// 跳到終點時,返回當前走的路徑長度,即爲最短路徑
return step[cur.x][cur.y];
}
for (int i = 0; i < 8; i++) { // 沒跳到終點,繼續遍歷當前位置的下一跳
next.x = cur.x + direction[i][0];
next.y = cur.y + direction[i][1];
if (next.x < 1 || next.x > 8 || next.y < 1 || next.y > 8) continue;
if (step[next.x][next.y] == 0) { // 若當前點未被訪問,則將其加入隊列
step[next.x][next.y] = step[cur.x][cur.y] + 1;
que.push(next);
}
}
}
}
int main() {
Point start, end;
cin >> start.x >> start.y;
cin >> end.x >> end.y;
cout << "The shortest path length is " << BFS(start, end) << endl;
}
6. 矩陣中的路徑
題目描述
請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符
的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,
向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之後不能
再次進入這個格子。 例如上圖:abtgcfcsjdeh 這樣的3 X 4 矩陣中包
含一條字符串"bfce"的路徑,但是矩陣中不包含"abfb"路徑,因爲字符串的第一
個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。
輸入:
3
4
abtgcfcsjdeh
bfce
abfb
輸出:
1
0
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
#define MAX_SIZE 1000
/**
* 思想:
* 當矩陣座標中爲(row, col)的格子和路徑字符串中下標爲pathLength的字符串一樣時,並且
* 該格子未被訪問時,則從4個相鄰的格子(row, col-1)、(row, col+1)、(row-1, col)、(row+1, col)
* 中去定位路徑字符串中下標爲pathLength+1的字符。
* 如果4個相鄰的格子都沒有匹配字符串中下標爲pathLength+1的字符,則表明當前路徑字符串中
* 下標爲pathLength的字符在矩陣中定位不正確,我們需要回到前一個字符(pathLength-1),然後重新定位。
* 一直重複這個過程,知道路徑字符串上的所有字符都在矩陣中找到合適的位置(此時 str[pathLength] == '\0')
*/
bool Judge(const char *matrix, int rows, int cols, int row, int col,
const char *str, int pathLength, bool *visited) {
if (str[pathLength] == '\0') // 遍歷完路徑字符串,說明路徑字符串上的所有字符都在矩陣中找到合適的位置了
return true;
bool hasPath = false;
int index = row * cols + col;
if (row >= 0 && row < rows && col >= 0 && col < cols
&& matrix[index] == str[pathLength]
&& !visited[index]) { // str[pathLength]字符找到了
visited[index] = true; // 並將找到的這個矩陣元素標識爲已訪問
hasPath = Judge(matrix, rows, cols, row, col-1, str, pathLength+1, visited)
|| Judge(matrix, rows, cols, row, col+1, str, pathLength+1, visited)
|| Judge(matrix, rows, cols, row-1, col, str, pathLength+1, visited)
|| Judge(matrix, rows, cols, row+1, col, str, pathLength+1, visited);
// 沒有匹配,回退上一個位置
visited[index] = false;
}
return hasPath;
}
bool hasPath(char *matrix, int rows, int cols, char *str) {
if (matrix == NULL || rows < 1 || cols < 1 || str == NULL)
return false;
bool *visited = new bool[rows * cols]; // 創建一個bool類型的動態數組,返回數組的第一個元素類型的指針
memset(visited, 0, rows * cols); // 將bool數組元素都初始化爲0,即false
int pathLength = 0; // 路徑字符串下標
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
if (Judge(matrix, rows, cols, row, col, str, pathLength, visited)) {
return true;
}
}
}
delete[] visited;
return false;
}
int main() {
char matrix[MAX_SIZE], str[MAX_SIZE];
int rows, cols, res;
printf("Please input the row number:\n");
scanf("%d", &rows);
printf("Please input the col number:\n");
scanf("%d", &cols);
getchar(); // 讀取緩衝區中的回車
printf("Please input the matrix:\n");
scanf("%s", matrix);
printf("Please input the search path:\n");
getchar(); // 讀取緩衝區中的回車
scanf("%s", str);
res = hasPath(matrix, rows, cols, str);
if (res) {
printf("Matirx has this path!\n");
} else {
printf("Matirx do not has this path!\n");
}
// getchar();
return 0;
}
7. 機器人的運動範圍
題目描述
地上有一個m行和n列的方格。一個機器人從座標0, 0的格子開始移動,每一次只能
向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k
的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。
但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?思路:
回溯的思想(類似於DFS深度優先搜索,先序遍歷,結合國際象棋看)
#include <stdio.h>
#include <stdlib.h>
/**
* 計算整數各位之和
*/
int GetDigistSum(int number) {
int sum = 0;
while (number) {
sum += (number % 10);
number /= 10;
}
return sum;
}
/**
* DFS 深度優先遍歷
*/
int Moving(int threshold, int rows, int cols, int x, int y, int *visited) {
int count = 0;
if (x >= 0 && x < rows && y >= 0 && y < cols
&& (GetDigistSum(x) + GetDigistSum(y)) <= threshold
&& visited[x * cols + y] == 0) {
visited[x * cols + y] = 1;
count = 1 + Moving(threshold, rows, cols, x-1, y, visited)
+ Moving(threshold, rows, cols, x+1, y, visited)
+ Moving(threshold, rows, cols, x, y-1, visited)
+ Moving(threshold, rows, cols, x, y+1, visited);
}
return count;
}
/**
* 初始化訪問標誌矩陣,並從 (0, 0) 開始遍歷
*/
int MovingCount(int threshold, int rows, int cols) {
if (threshold < 0 || rows < 0 || cols < 0)
return 0;
int *visited = (int*)malloc(sizeof(int) * rows * cols);
for (int i = 0; i < rows * cols; i++)
*(visited + i) = 0;
int count = Moving(threshold, rows, cols, 0, 0, visited);
free(visited);
return count;
}
int main() {
int rows, cols, threshold = 18;
scanf("%d %d", &rows, &cols);
scanf("%d", &threshold);
printf("%d\n", MovingCount(threshold, rows, cols));
return 0;
}
8. 全排列 [leetcode-46]
123的全排列有123、132、213、231、312、321這六種。首先考慮213和321
這二個數是如何得出的。顯然這二個都是123中的1與後面兩數交換得到的。然後
可以將123的第二個數和每三個數交換得到132。同理可以根據213和321來得231
和312。因此可以知道——全排列就是從第一個數字起每個數分別與它後面的數字交
換。找到這個規律後,遞歸的代碼就很容易寫出來了
#include <stdio.h>
/* 交換兩個數據 */
void Swap(int* a, int* b) {
int c = *a;
*a = *b;
*b = c;
}
void FullyArrange(int str[], int index, int str_len) {
if (index == str_len) {
/* 輸出當前的排列 */
for (int i = 0; i < str_len; i++) {
printf("%d ", str[i]);
}
printf("\n");
} else {
for (int j = index; j < str_len; j++) { // 第index個數分別與它後面的數字交換就能得到新的排列
Swap(&str[index], &str[j]);
FullyArrange(str, index + 1, str_len);
Swap(&str[index], &str[j]); // 保證每一層遞歸後保持上一層的順序
}
}
}
int main() {
int a[] = {1, 2, 3};
int str_len = sizeof(a) / sizeof(int);
FullyArrange(a, 0, str_len);
return 0;
}
9. 全排列II [leetcode-47]
給定一個可包含重複數字的序列,返回所有不重複的全排列。
輸入: [1,1,2]
輸出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
#include <iostream>
#include <vector>
#include <set>
using namespace std;
void Swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void permuteUniqueDFS(vector<int> &nums, int index, set<vector<int>> &res_set) {
if (index == nums.size()) {
res_set.insert(nums);
}
for (int i = index; i < nums.size(); i++) {
Swap(nums[i], nums[index]);
permuteUniqueDFS(nums, index + 1, res_set);
Swap(nums[i], nums[index]);
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
set<vector<int>> res_set;
permuteUniqueDFS(nums, 0, res_set);
for (set<vector<int>>::iterator iter = res_set.begin(); iter != res_set.end(); iter++) {
res.push_back(*iter);
}
return res;
}
int main() {
vector<int> nums;
int x;
while (cin >> x && x != -1) {
nums.push_back(x);
}
vector<vector<int>> res = permuteUnique(nums);
for (int i = 0; i < res.size(); i++) {
for (int j = 0; j < res[0].size(); j++) {
cout << res[i][j] << " ";
}
cout << endl;
}
return 0;
}
10. 組合 [leetcode-77]
給定兩個整數 n 和 k,返回 1 … n 中所有可能的 k 個數的組合。
輸入: n = 4, k = 2
輸出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
#include <iostream>
#include <vector>
using namespace std;
/**
* 像這種要求出所有結果的集合,一般都是用DFS調用遞歸來解(結合全排列等)
*/
void DFS(int n, int k, int level, vector<int> &out, vector<vector<int>> &res) {
if (out.size() == k) {
res.push_back(out);
return;
}
for (int i = level; i <= n; i++) {
out.push_back(i);
DFS(n, k, i + 1, out, res);
out.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> out;
DFS(n, k, 1, out, res);
return res;
}
int main() {
int n, k;
cin >> n >> k;
vector<vector<int>> nums = combine(n, k);
for (vector<vector<int>>::iterator iter = nums.begin(); iter != nums.end(); iter++) {
vector<int>::iterator it = (*iter).begin();
for (; it != (*iter).end(); it++) {
cout << *it << " ";
}
cout << endl;
}
return 0;
}
11. 電話號碼字母組合 [leetcode-17]
給定一個僅包含數字 2-9 的字符串,返回所有它能表示的字母組合。
給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。
輸入:“23”
輸出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].思路:
我們用遞歸 Recursion 來解,我們需要建立一個字典,用來保存每個數字所代表的字符串,
然後我們還需要一個變量 level,記錄當前生成的字符串的字符個數,實現套路和上述那些
題十分類似。在遞歸函數中我們首先判斷 level,如果跟 digits 中數字的個數相等了,
我們將當前的組合加入結果 res 中,然後返回。否則我們通過 digits 中的數字到 dict
中取出字符串,然後遍歷這個取出的字符串,將每個字符都加到當前的組合後面,並調用遞歸
函數即可
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void letterCombinationsDFS(string &digist, vector<string> &dict, int index, string out, vector<string> &res) {
if (index == digist.size()) {
res.push_back(out);
return;
}
string str = dict[digist[index] - '0'];
for (int i = 0; i < str.size(); i++) {
letterCombinationsDFS(digist, dict, index + 1, out + str[i], res);
}
}
vector<string> letterCombinations(string digits) {
if (digits.empty()) return {};
vector<string> res;
vector<string> dict{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
letterCombinationsDFS(digits, dict, 0, "", res);
}
int main() {
string digits;
cin >> digits;
vector<string> res = letterCombinations(digits);
for (vector<string>::iterator iter = res.begin(); iter != res.end();
iter++) {
cout << *iter << endl;
}
return 0;
}
12. 組合總和 [leetcode-39]
給定一個無重複元素的數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和爲 target 的組合。
candidates 中的數字可以無限制重複被選取。說明:
所有數字(包括 target)都是正整數。
解集不能包含重複的組合。
示例 1:
輸入: candidates = [2,3,6,7], target = 7,
所求解集爲:
[
[7],
[2,2,3]
]示例 2:
輸入: candidates = [2,3,5], target = 8,
所求解集爲:
[
[2,2,2,2],
[2,3,3],
[3,5]
]思路:和組合,排列的那些題目都是一個套路
需要另寫一個遞歸函數,這裏我們新加入三個變量,index記錄當前的遞歸到的下標,out爲一個解,res保存所有已經得到的解,每次調用新的遞歸函數時,此時的target要減去當前數組的的數
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void combinationSumDFS(vector<int> candidates, int target, int index, vector<int> &out, vector<vector<int>> &res) {
if (target < 0) return;
if (target == 0) {
res.push_back(out);
return;
}
for (int i = index; i < candidates.size(); i++) {
out.push_back(candidates[i]);
combinationSumDFS(candidates, target - candidates[i], i, out, res);
// 注意,這裏傳入的是 i 而不是 index,保證以前重複使用過的不再回溯使用,即保證沒有重複的組合
out.pop_back(); // 如果加上下一個之後大於target了,則回溯到上一步
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> out;
combinationSumDFS(candidates, target, 0, out, res);
return res;
}
int main() {
vector<vector<int>> res;
vector<int> candidates;
int target, x;
while (cin >> x && x != -1) {
candidates.push_back(x);
}
cin >> target;
res = combinationSum(candidates, target);
for (int i = 0; i < res.size(); i++) {
cout << "[";
for (int j = 0; j < res[i].size(); j++) {
cout << res[i][j] << " ";
}
cout << "]" << endl;
}
return 0;
}
13. 組合總和II [leetcode-40]
給定一個數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使
數字和爲 target 的組合。candidates 中的每個數字在每個組合中只能使用一次。說明:
所有數字(包括目標數)都是正整數。
解集不能包含重複的組合。
示例 1:輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集爲:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:輸入: candidates = [2,5,2,1,2], target = 5,
所求解集爲:
[
[1,2,2],
[5]
]思路:和組合,排列的那些題目都是一個套路
需要另寫一個遞歸函數,這裏我們新加入三個變量,index記錄當前的遞歸到的下標,
out爲一個解,res保存所有已經得到的解,每次調用新的遞歸函數時,此時的target
要減去當前數組的的數
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void combinationSum2DFS(vector<int> &candidates, int target, int index, vector<int> &out, vector<vector<int>> &res) {
if (target < 0) return;
if (target == 0) {
res.push_back(out);
return;
}
for (int i = index; i < candidates.size(); i++) {
if (i > index && candidates[i] == candidates[i-1]) continue; // 保證res中沒有重複的
out.push_back(candidates[i]);
combinationSum2DFS(candidates, target - candidates[i], i + 1, out, res);
// i + 1,就不會重複使用當前數字了
out.pop_back();
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> out;
sort(candidates.begin(), candidates.end());
combinationSum2DFS(candidates, target, 0, out, res);
return res;
}
int main() {
vector<vector<int>> res;
vector<int> candidates;
int target, x;
while (cin >> x && x != -1) {
candidates.push_back(x);
}
cin >> target;
res = combinationSum2(candidates, target);
for (vector<vector<int>>::iterator iter = res.begin(); iter != res.end(); iter++) {
cout << "[";
for (vector<int>::iterator it = (*iter).begin(); it != (*iter).end(); it++) {
cout << *it << " ";
}
cout << "]" << endl;
}
return 0;
}
14. 組合總和III [leetcode-216]
找出所有相加之和爲 n 的 k 個數的組合。組合中只允許含有 1 - 9 的正整數,並且每種組合中不存在重複的數字。
說明:
所有數字都是正整數。
解集不能包含重複的組合。示例 1:
輸入: k = 3, n = 7
輸出: [[1,2,4]]示例 2:
輸入: k = 3, n = 9
輸出: [[1,2,6], [1,3,5], [2,3,4]]
#include <iostream>
#include <vector>
using namespace std;
void combinationSum3DFS(int k, int target, int index, vector<int> &out, vector<vector<int>> &res) {
if (target < 0) return;
if (target == 0 && out.size() == k) {
res.push_back(out);
return;
}
for (int i = index; i <= 9; i++) {
out.push_back(i);
combinationSum3DFS(k, target - i, i + 1, out, res); // 注意,這裏要傳入 i + 1,保證不適用重複的元素
out.pop_back();
}
}
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> out;
combinationSum3DFS(k, n, 1, out, res);
return res;
}
int main() {
int k, n;
cin >> k >> n;
vector<vector<int>> res = combinationSum3(k, n);
for (vector<vector<int>>::iterator iter = res.begin(); iter != res.end(); iter++) {
cout << "[";
for (vector<int>::iterator it = (*iter).begin(); it != (*iter).end(); it++) {
cout << *it << " ";
}
cout << "]" << endl;
}
return 0;
}
15. 子集 [leetcode-78]
給定一組不含重複元素的整數數組 nums,返回該數組所有可能的子集(冪集)。
說明:解集不能包含重複的子集。輸入: nums = [1,2,3]
輸出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]思想:
其實就是將長度爲0-nums.size() 長度的組合都列出來就可以。
#include <iostream>
#include <vector>
using namespace std;
void subsetsDFS(vector<int> &nums, int index, int target_size, vector<int> &out, vector<vector<int>> &res) {
if (out.size() == target_size) {
res.push_back(out);
return;
}
for (int i = index; i < nums.size(); i++) {
out.push_back(nums[i]);
subsetsDFS(nums, i + 1, target_size, out, res);
out.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> out;
res.push_back({});
for (int i = 1; i <= nums.size(); i++) {
subsetsDFS(nums, 0, i, out, res);
}
return res;
}
int main() {
vector<vector<int>> res;
vector<int> nums;
int x;
while (cin >> x && x != -1) {
nums.push_back(x);
}
res = subsets(nums);
for (vector<vector<int>>::iterator iter = res.begin(); iter != res.end(); iter++) {
cout << "[";
for (vector<int>::iterator it = (*iter).begin(); it != (*iter).end(); it++) {
cout << *it << " ";
}
cout << "]" << endl;
}
return 0;
}