写在前面
写了n多个递归问题,总结下:
解决步骤:
1. 把问题分解成可以递归的步骤:一个步骤的,递归里套一个递归(如计算n!) ;多个步骤的,套多个递归(如数字左右移动的,左走递归一次,右走递归一次)
2. 确定递归结束条件,这个作为递归的结束符号,一定要想清楚,每种情况都要考虑到!
3. 递归传递参数的确定,它可以是递归用的参数(可变,如 index 标签),也可以是之前设定的不变的参数,还可以是递归函数的返回量(一般用&引用)
解答技巧:
1. 通常要解决的递归函数中没有递归参量的设定,所以一般要在该递归函数内设定参量,再在里面写真正的递归函数,这就是为什么很多题目至少要写两个函数。
2. 递归问题框架一般是 recfun(){
if (xxx) return xx; //递归结束条件
if(xxx){xxx}; //自己设计的功能,看情况写
if(走这步递归的条件)recfun();
if (走这步递归的条件) recfun(); // n个步骤写n个递归, 递归参数发生改变
return xx;
}
任务地址:https://see.stanford.edu/materials/icspacs106b/H18-Assign3RecPS.pdf
1. The 12-step program to recursive enlightenment
这个还是比较简单,以前也写过
#include <iostream>
using namespace std;
int CountWays(int numStaurs);
int main()
{
int m = 10;
cout<<"The numStaurs is "<<m<<endl;
cout<< "The CountWays is "<<CountWays(m) <<endl;
return 0;
}
int CountWays(int numStaurs) {
if (numStaurs == 1)
return 1;
if (numStaurs == 2)
return 2;
else return CountWays(numStaurs - 1) + CountWays(numStaurs - 2);
}
/*
结果展示:
The numStaurs is 10
The CountWays is 89
*/
2. Ruler of the world
递归画图,无奈图形库用不了,就看看了
#include "genlib.h"
#include "graphics.h"
#include <iostream>
#include "extgraph.h"
void DrawRuler(double x, double y, double w, double h);
void RecDrawRuler(double x, double y, double w, double h);
int main ()
{
InitGraphics();
double x,y = 0;
double width = GetWindowWidth();
double height = GetWindowHeight()/3;
DrawRuler(x,y,width,height);
return 0;
}
void DrawRuler(double x, double y, double w, double h)
{
MovePen(x,y);
DrawLine(w, 0); // draw horizontal line along base
RecDrawRuler(x,y,w,h); // recursively draw vertical lines
}
void RecDrawRuler(double x, double y, double w, double h)
{
if (h < .05) return;
double middle = (x/2)+(w/2);
MovePen(middle, y); // move pen to center of X-axis
DrawLine(0,h);
RecDrawRuler(x, y, middle, h/2); // draw left
RecDrawRuler(middle, y, w, h/2); // draw right
}
3. Every vote counts
计算是关键票情况的题目,哎看题目就看了好久,想法写在代码中了
#include "genlib.h"
#include <iostream>
#include "vector.h"
#include "simpio.h"
using namespace std;
int CountCriticalVotes(Vector<int> &blocks, int blockIndex);
int RecVote(Vector<int>&blocks, int index, int sum, int &count, int & vote, int totalVotes);
int main(){
Vector<int> blocks;
int blockIndex;
//加入数,输入0表示停止输入
while(true){
cout << "Enter vote blocks (enter 0 when finished):";
int num = GetInteger();
if(num == 0) break;
blocks.add(num);
}
//输入index
while (true){
cout<<"Enter block index : ";
blockIndex = GetInteger();
if (blockIndex < blocks.size()) break;
else cout << "Invalid Index"<< endl ;
}
//计算结果
int votes = CountCriticalVotes(blocks,blockIndex);
cout << "Block index " << blockIndex << " has " << votes << " criticals votes. " << endl;
return 0;
}
int CountCriticalVotes(Vector<int> &blocks, int blockIndex){
int vote = blocks[blockIndex];
//得到除去index的新vector
blocks.removeAt(blockIndex);
//递归函数需要的初始化参量
int count = 0; //最后需要返回的值,也就要求的关键vote的次数
int index = 0; //block选取索引
int sum = 0; //计算投票给A的票数
int totalVotes = 0; //计算总票数(除去vote),用于计算投票给B的票数
for (int i=0; i< blocks.size();i++){
totalVotes += blocks[i];
}
return RecVote(blocks, index, sum, count, vote, totalVotes);
}
//最关键的递归函数,和天平砝码计算那个例子有异曲同工之妙
//index作索引,一步步给投票,每步递归造成 index+1,sum改动
//递归最小时,通过参量vote, sum, totalVotes,判断是否为关键票,造成count的变化
//个人认为递归设计最难的是递归函数参量的设计,再在递归最小时进行需要的操作
//因此设计递归一般两个函数,第一个函数声明递归函数需要的参量,并初始化,第二个函数输入参量进行递归运算。
int RecVote(Vector<int>&blocks, int index, int sum, int &count, int & vote, int totalVotes){
//递归结束条件
if(index >= blocks.size()){
int sumA = sum;
int sumB = totalVotes - sum;
if(
(sumB > sumA && sumA + vote > sumB) ||
(sumA > sumB && sumB + vote > sumA)
)
count ++;
return count;
}
//给A投票
RecVote(blocks, index+1,sum+blocks[index],count,vote,totalVotes);
//给B投票
RecVote(blocks, index+1,sum,count,vote,totalVotes);
return count; //大神写的没这步~所以结果不对
}
结果展示:
/*
//John Banzhaf III的例子,后3个blocks对结果没影响
Enter vote blocks (enter 0 when finished):9
Enter vote blocks (enter 0 when finished):9
Enter vote blocks (enter 0 when finished):7
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):1
Enter vote blocks (enter 0 when finished):1
Enter vote blocks (enter 0 when finished):0
Enter block index : 3
Block index 3 has 0 criticals votes.
//一种极端情况,可以看出结果符合2的n次方
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):3
Enter vote blocks (enter 0 when finished):8888
Enter vote blocks (enter 0 when finished):0
Enter block index : 9
Block index 9 has 512 criticals votes.
*/
4. Cell phone mind reading
用到两个递归,一个是section2的最后一题,再加一个补全单词的递归
#include <iostream>
#include "genlib.h"
#include "simpio.h"
#include <string>
#include "strutils.h"
#include <ctype.h>
#include "lexicon.h"
//函数多加了个输入lex
void ListCompletions(string digitSequence, Lexicon & lex);
void ReLisCompletions(string prefix, string rest,Lexicon & lex);
string DigitLetters(char ch);
//新增的函数,输入prefix,lex 得到并打印完整的单词
void RecursiveCompletion(Lexicon &lex, string prefix);
//for循环该字符串
const string alphabet = "abcdefghijklnmopqrstuvwxyz";
int main ()
{
Lexicon lex("lexicon.dat");
ListCompletions("72547",lex);
return 0;
}
void ListCompletions(string digitSequence, Lexicon & lex){
ReLisCompletions("", digitSequence,lex);
}
void ReLisCompletions(string prefix, string rest,Lexicon & lex){
if (rest.size()==0)
{
RecursiveCompletion(lex, prefix);//在手机打字的基础上主要改这一步
}
else{
string opt = DigitLetters(rest[0]);
for (int i=0; i<opt.length(); i++)
{
ReLisCompletions(prefix + opt[i], rest.substr(1),lex);
}
}
}
string DigitLetters(char ch)
{
switch (ch) {
case '2': return ("abc");
case '3': return ("def");
case '4': return ("ghi");
case '5': return ("jkl");
case '6': return ("mno");
case '7': return ("pqrs");
case '8': return ("tuv");
case '9': return ("wxyz");
default: Error("Illegal digit");
}
}
void RecursiveCompletion(Lexicon &lex, string prefix) {
//是单词直接输出,递归最小单位
if (lex.containsWord(prefix)) {
cout << ConvertToLowerCase(prefix) << endl;
}
//补字母
for (int i = 0; i < alphabet.length(); i++) {
//containsPrefix检测前缀是否存在,简化递归效率
if (lex.containsPrefix(prefix + alphabet[i]))
// add letters to test if completion exist in lexicon
RecursiveCompletion(lex, prefix + alphabet[i]);
}
}
效果展示:输入的是72547
palisade
palisaded
palisades
palisading
palish
rakis
rakish
rakishly
rakishness
sakis
5. A recursive puzzle
本题关键是解决递归结束判断,用set类存放去过的地方,一旦回到去过的地方递归结束。
所以遇到想解决 只判断执行某个问题一次时,可以用set类存储,判断是否在set类中,来实现。
#include <iostream>
#include <string>
#include "vector.h"
#include "set.h"
using namespace std;
bool Solvable(int start, Vector<int> & squres);
bool ReSolvable(int start,Vector<int> & squres, Set<int> &set, bool & solved);
int main(){
Vector<int> squres;
//填数
int start = 0;
squres.add(3);squres.add(6);squres.add(4);squres.add(1);squres.add(3);
squres.add(4);squres.add(2);squres.add(5);squres.add(3);squres.add(0);
bool solve = Solvable(start, squres);
cout <<solve<< endl;
return 0;
}
//同样,在函数中新建递归函数
bool Solvable(int start, Vector<int> & squres){
bool solved = false;
Set<int> set;
return ReSolvable(start,squres,set,solved);
}
//set用于判断该处是否去过,避免一直循环,也是判断是否结束递归的重要条件
bool ReSolvable(int start,Vector<int> &squres, Set<int> &set,bool & solved){
//递归结束判断
if(set.contains(start))
return false;
if(start == squres.size()-1){
solved = true;
return true;
}
set.add(start);
//递归操作,因为可以向左也可以向右,所以有两步
if(start + squres[start] < squres.size()) {
ReSolvable(start + squres[start], squres, set, solved);
}
if(start - squres[start] >= 0) {
ReSolvable(start - squres[start], squres, set, solved);
}
return solved;
}
6. Stock cutting
递归最后一个任务,任务上写着 you can proudly say you have earned you CS106 Recursion Merit Badge!
于是这篇完全独立完成,花了挺多时间,挺笨的,但还是解决了问题
用了两个递归函数
#include <iostream>
#include "vector.h"
#include <string>
using namespace std;
int CutStock(Vector<int> & requests, int stockLength);
int Delete(Vector<int> &requests, int stockLength, int num, int & max, int i,string strtemp, string & str);
int main(){
//自己设定vector
Vector<int> myvec;
myvec.add(4);myvec.add(3);myvec.add(4);
myvec.add(1);myvec.add(7);myvec.add(8);
//设定stocklengh
int stockLength = 10;
cout <<"The number of stock pipes needed is "<< CutStock(myvec , stockLength) << endl;
return 0;
}
int CutStock(Vector<int> & requests, int stockLength){
//递归截止条件
if(requests.size() == 1)
return 1;
if(requests.size() == 0)
return 0;
int num = 0;
int max = 0;
int i = 0;
string str = "";
Delete(requests, stockLength, num, max, i, "", str);//获得选出的index字符串
//通过选出的字符串获得新的request数组
for(int j = 0; j< str.size(); j++)
requests[ int(str[j]) - 48 ]=0;
for(int j = requests.size()-1; j >= 0 ; j--){
if(requests[j] == 0)
requests.removeAt(j);
}
//将新的request输入完成递归
return CutStock(requests,stockLength) + 1;
}
int Delete(Vector<int> &requests, int stockLength, int num, int & max, int i,string strtemp,string & str){
if(num > stockLength)
return 0;
else if(num > max) {
max = num;
str = strtemp;
}
if(i>requests.size() -1 )
return 0;
Delete(requests, stockLength, num + requests[i], max, i + 1,strtemp + char(i+48),str);
Delete(requests, stockLength, num , max, i + 1,strtemp,str);
return 0;
}
结果展示:
The number of stock pipes needed is 3