前言
1.汉诺塔问题
尝试时不要考虑全局问题,给所有过程定一个统一的标准,在局部上拆解问题
代码实现:
void func(int i, string start, string end, string other) {
if (i == 1) {//base case
cout << "move 1 from " << start << " to " << end << endl;
}
else {
func(i - 1, start, other, end);
cout << "move " << i << " from " << start << " to " << end << endl;
func(i - 1, other, end, start);
}
}
void hanoi(int n) {
if (n > 0) {
func(n, "左", "右", "中");
}
}
2.打印一个字符串的全部子序列,包括空字符串
每个字符都可以选择要或者不要两种情况:
子序列的字符相对位置不能改变,个数可以不同
代码实现:
void process(string& s, int i, string res) {//这里的res不能用地址传递
if (i == s.length()) {
cout << res << endl;
}
else {
process(s, i + 1, res);//不要s[i]
res += s[i];
process(s, i + 1, res);
}
}
void process(string& s, int i) {
if (i == s.length()) {
cout << s << endl;
}
else {
process(s, i + 1);//要s[i]
char temp = s[i];
s[i] = 0;//ASCII码中0表示空字符
process(s, i + 1);
s[i] = temp;
}
}
void printSubStr(string s) {
string res = "";
process(s, 0, res);
cout << endl;
process(s, 0);
}
3.全排列
全排列的字符相对位置可以改变,但字符的个数应与原字符串相同。
返回一个字符串的全部,要求不要出现重复的排列
代码实现:
void swap(string& str, int i, int j) {
char c = str[i];
str[i] = str[j];
str[j] = c;
}
//str[i..]范围上,所有的字符,都可以在i位置上,后续都去尝试
//str[0..i-1]范围上,是之前做的选择
//把所有字符串形成的全排列加入到res中
void process(string& str, int i, vector<string>& res) {
if (i == str.length()) {
res.push_back(str);
return;
}
vector<bool>visited(26, false);
for (int j = i; j < str.length(); j++) {
if (visited[str[j] - 'a'] == false) {
swap(str, i, j);
process(str, i + 1, res);
swap(str, j, i);
visited[str[j] - 'a'] = true;
}
}
}
vector<string> fullPermutation(string str) {
vector<string>res;
process(str, 0, res);
return res;
}
4.抽纸牌问题
代码实现:
int s(vector<int>& arr, int i, int j);
int f(vector<int>& arr, int i, int j) {
if (i == j) {
return arr[i];
}
return max((arr[i] + s(arr, i + 1, j)), (arr[j] + s(arr, i, j - 1)));
}
int s(vector<int>& arr, int i, int j) {
if (i == j) {
return 0;
}
return min( f(arr, i + 1, j), f(arr, i, j - 1));
}
int cardWin(vector<int>&arr) {
if (arr.size() == 0) {
return 0;
}
return max(f(arr, 0, arr.size() - 1), s(arr, 0, arr.size() - 1));
}
5.逆序栈
移出栈底元素并返回f:
reverse:
代码实现:
int f(stack<int>& sk) {//移出栈底元素并返回
int res = sk.top();
sk.pop();
if (sk.empty()) {
return res;
}
int last = f(sk);
sk.push(res);
return last;
}
void reverse(stack<int>& sk) {
if (sk.empty()) {
return;
}
int i = f(sk);
reverse(sk);
sk.push(i);
}
6.数字串转成字符串
从左到右试:[0…i-1]确定,[i…]可以变
(1)arr[i]==0 --> return 0
(2)arr[i]>=3 --> i自己转,return i+1的结果
(3)arr[i]==1 --> i自己转,return i+1的结果;i和i+1一块转,return i+2
(4)arr[i]==2 --> i自己转,return i+1的结果;在i和i+1组成的数字不超过26的情况下,i和i+1一块转,return i+2,否则该情况不存在
代码实现:
//i之前的位置,如何转化已经做出了决定
//i..有多少种转化结果
int process(string& s, int i) {
if (i == s.length()) {
return 1;
}
if (s[i] == '0') {
return 0;
}
if (s[i] == '1') {
int res = process(s, i + 1);//i自己作为单独的部分,后续有多少种方法
if (i + 1 < s.length()) {
res += process(s, i + 2);//i和i+1作为单独的部分,后续有多少种方法
}
return res;
}
if (s[i] == '2') {
int res = process(s, i + 1);//i自己作为单独的部分,后续有多少种方法
//i和i+1作为单独的部分并且没有超过26,后续有多少种方法
if (i + 1 < s.length() && s[i + 1] >= '0' && s[i + 1] <= '6') {
res += process(s, i + 2);
}
return res;
}
//s[i]=='3'~'9'
return process(s, i + 1);
}
int numToStr(string& s) {
return process(s, 0);
}
7.背包问题
从左往右试:找到可变参数最简单,可变参数最少的试法,容易改动态规划
代码实现:
int process(vector<int>& weights, vector<int>& values, int bag, int i, int alreadyWeight, int alreadyValue) {
if (alreadyWeight > bag) {
return 0;
}
if (i == values.size()) {
return alreadyValue;
}
return max(process(weights, values, bag, i + 1, alreadyWeight, alreadyValue),
process(weights, values, bag, i + 1, alreadyWeight + weights[i], alreadyValue + values[i])
);
}
int maxValue(vector<int>& weights, vector<int>& values,int bag) {
return process(weights, values, bag, 0, 0, 0);
}
8.八皇后问题
位运算优化:
列限制:00000000
左斜限制:00000000
右斜限制:00000000
代码实现:
//方法一:
//record[0..i-1]需要看,record[i..]不需要看
//返回i行的皇后放在j列是否有效
bool isValid(vector<int>& record, int i, int j) {
for (int k = 0; k < i; k++) {
if (j == record[k] || abs(j - record[k]) == abs(i - k)) {
return false;
}
}
return true;
}
int process01(int i, vector<int>& record, int n) {
if (i == n) {
return 1;
}
int res = 0;
for (int j = 0; j < n; j++) {
//当前i行皇后放在j列会不会和之前的0..i-1的皇后共行或者共列或者共斜线
//如果是,认为无效
//如果不是,认为有效
if (isValid(record, i, j)) {
record[i] = j;
res += process01(i + 1, record, n);
}
}
return res;
}
int nQueens01(int n) {
if (n <= 0) {
return 0;
}
vector<int>record(n);//第i行皇后所在的列
return process01(0, record, n);
}
//方法二:位运算优化
//colLim列限制:1位置不能放皇后,0位置可以
//leftLim左斜限制:...
//rightLim右斜限制:...
int process02(int limit, int colLim, int leftLim, int rightLim) {
if (colLim == limit) {
return 1;
}
int pos = 0;//可以点皇后的位置
int mostRightOne = 0;
pos = limit & (~(colLim | leftLim | rightLim));
int res = 0;
while (pos != 0) {
mostRightOne = pos & ((~pos) + 1);
pos = pos - mostRightOne;
res += process02(limit, colLim | mostRightOne, (leftLim | mostRightOne) << 1, (rightLim | mostRightOne) >> 1);
}
return res;
}
int nQueens02(int n) {
if (n < 1 || n>32) {
return 0;
}
int limit = n == 32 ? -1 : (1 << n) - 1;
return process02(limit, 0, 0, 0);
}