左神课程笔记——第八节课:暴力递归


前言

在这里插入图片描述


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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值