暴力递归是动态规划的基础。
暴力递归其实就是尝试。
暴力递归的过程:
1. 把问题转化为规模缩小了的同类问题的子问题;
2. 有明确的不需要继续递归的条件(base case);
3. 有当得到子问题的结果后的决策过程;
4. 不记录每一个子问题的解。
例题:
1. 汉诺塔问题:打印n层汉诺塔从最左边移动到最右边的全部过程。
void func(int rest,int down,string from,string help,string to){
if(rest <= 1){
cout <<" move " << down << " from " << from << " to " << to << endl;
return;
}
//将除了最底层以外的圆盘移动到中间
func(rest - 1, down - 1,from,to,help);
//将最底层的圆盘移动到右边
func(1,down,from,help,to);
//将其余圆盘移动回左边
func(rest - 1, down - 1,help,from,to);
}
void hanoi(int n){
if(n > 0){
func(n,n,"left","mid","right");
}
}
2. 打印一个字符串的全部子序列,包括空字符串。
//打印字符串的全部子序列
//通过完全二叉树(一层代表一个字符),每个路径都选择是否要该字符
//每到达一个叶子节点就是一个子序列
void func(char chs[],string s,int n,int i){
if(i >= n){
cout << s << endl;
return;
}
string str(s);
str += chs[i];
func(chs,str,n,i + 1);
func(chs,s,n,i + 1);
}
//节省空间的方法
void func(string chs,int n,int i){
if(i == n){
cout << chs << endl;
return;
}
func(chs,n,i + 1);
char temp = chs[i];
chs.erase(i - (n - chs.size()),1);
func(chs,n,i + 1);
chs.insert(i - (n - chs.size()),&temp);
}
void printStr(string str,int n){
if( n <= 1){
return;
}
// string s;
// func(str,s,n,0);
func(str,n,0);
}
3. 打印一个字符串的全部排列;
打印一个字符串的全部排列,要求不出现重复的过程。
//打印一个字符串的全部排列
void func(string s,int i){
if(i == s.size()){
cout << s << endl;
return;
}
for(int j = i; j < s.size(); j++){
swap(s[i],s[j]);
func(s,i + 1);
swap(s[i],s[j]);
}
}
//不重复的打印一个字符串的全部排列
//这里有两种方法,一种是在上个方法的基础上,对结果进行清洗
//另一种是在排列过程中进行分支限界,推荐后者。
void func_2(string s,int i){
if(i == s.size()){
cout << s << endl;
return;
}
bool res[26] = {false};
for(int j = i; j < s.size(); j++){
if(!res[(int)(s[j] - 'a')]){
res[(int)(s[j] - 'a')] = true;
swap(s[i],s[j]);
func_2(s,i + 1);
swap(s[i],s[j]);
}
}
}
void printPermutation(string s)
{
if(s.size() <= 0){
return;
}
func_2(s,0);
return;
}
4. 给定一个整型数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸 牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A 和玩家B都绝顶聪明。请返回最后获胜者的分数。
【举例】 arr=[1,2,100,4]。 开始时,玩家A只能拿走1或4。如果开始时玩家A拿走1,则排列变为[2,100,4],接下来 玩家 B可以拿走2或4,然后继续轮到玩家A... 如果开始时玩家A拿走4,则排列变为[1,2,100],接下来玩家B可以拿走1或100,然后继 续轮到玩家A... 玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100。所以玩家A会先拿1, 让排列变为[2,100,4],接下来玩家B不管怎么选,100都会被玩家 A拿走。玩家A会获胜, 分数为101。所以返回101。 arr=[1,100,2]。 开始时,玩家A不管拿1还是2,玩家B作为绝顶聪明的人,都会把100拿走。玩家B会获胜, 分数为100。所以返回100。
//玩纸牌
int second(int arr[],int L,int R);
int first(int arr[],int L,int R);
int win(int arr[],int n){
if(arr == NULL || n <= 0){
return 0;
}
return max(first(arr,0,n - 1),second(arr,0,n - 1));
}
//先手
int first(int arr[],int L,int R){
if( L == R){
return arr[L];
}
return max(arr[L] + second(arr,L + 1,R),arr[R] + second(arr,L,R - 1));
}
//后手
int second(int arr[],int L,int R){
if(L == R){
return 0;
}
return min(first(arr,L + 1, R),first(arr,L,R - 1));
}
5. 给你一个栈,请你逆序这个栈,不能申请额外的数据结构,只能使用递归函数。 如何实现?
/逆序一个栈,不能使用额外的数据结构,只能使用递归
int getStackButton(stack<int>& s){
int res = s.top();
s.pop();
if(s.empty()){
return res;
}
int last = getStackButton(s);
s.push(res);
return last;
}
void reverseStack(stack<int>& s){
if(s.empty()){
return;
}
int num = getStackButton(s);
reverseStack(s);
s.push(num);
}
6. 规定1和A对应、2和B对应、3和C对应... 那么一个数字字符串比如"111",就可以转化为"AAA"、"KA"和"AK"。 给定一个只有数字字符组成的字符串str,返回有多少种转化结果。
//6. 规定1和A对应、2和B对应、3和C对应...
// 那么一个数字字符串比如"111",就可以转化为"AAA"、"KA"和"AK"。 给定一个只有数字字符组成的字符串str,返回有多少种转化结果。
int func(int arr[],int n,int i){
if( i == n){
return 1;
}
if(arr[i] == 0){
return 0;
}
if(arr[i] == 1){
int res = (i + 1 < n) ? func(arr,n,i + 1) + func(arr,n,i + 2) : func(arr,n,i + 1);
return res;
}
else if(arr[i] == 2){
int res = func(arr,n,i + 1);
if(i + 1 < n && arr[i + 1] <= 6 ){
res += func(arr,n, i + 2);
}
return res;
}
int res = func(arr,n,i + 1);
return res;
}
7. 给定两个长度都为N的数组weights和values,weights[i]和values[i]分别代表 i号物品的重量和价值。给定一个正数bag,表示一个载重bag的袋子,你装的物 品不能超过这个重量。返回你能装下最多的价值是多少?
int func(int weight[],int values[],int n,int i,int alreadyWeight,int bagWeight,int value){
if( alreadyWeight > bagWeight ){
return 0;
}
if(i == n){
return value;
}
int res_1 = func(weight,values,n,i + 1,alreadyWeight,bagWeight,value);
int res_2 = func(weight,values,n, i + 1,alreadyWeight + weight[i],bagWeight,value + values[i]);
return max(res_1,res_2);
}
int maxValues(int weight[],int values[],int n,int bagWeight){
return func(weight,values,n,0,0,bagWeight,0);
}
8. N皇后问题是指在N*N的棋盘上要摆N个皇后,要求任何两个皇后不同行、不同列, 也不在同一条斜线上。 给定一个整数n,返回n皇后的摆法有多少种。 n=1,返回1。 n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0。 n=8,返回92。
//n皇后问题
bool isValid(int* record,int i,int j){
for(int k = 0; k < i; k++){
if(record[k] == j || abs(record[k] - j) == abs(i - k)){
return false;
}
}
return true;
}
int func(int n,int i,int* record){
if( i == n ){
return 1;
}
int res = 0;
for(int j = 0; j < n; j++){
if(isValid(record,i,j)){
record[i] = j;
res += func(n, i+ 1,record);
}
}
return res;
}
int nQueens(int n){
if( n <= 1){
return 1;
}
int record[10];
return func(n,0,record);
}