CS106B Assignment #3

写在前面

写了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

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值