期末复习之递归

期末复习之递归

1.完美覆盖

描述
一张普通的国际象棋棋盘,它被分成 8 乘 8 (8 行 8 列) 的 64 个方格。设有形状一样的多米诺牌,每张牌恰好覆盖棋盘上相邻的两个方格,即一张多米诺牌是一张 1 行 2 列或者 2 行 1 列的牌。那么,是否能够把 32 张多米诺牌摆放到棋盘上,使得任何两张多米诺牌均不重叠,每张多米诺牌覆盖两个方格,并且棋盘上所有的方格都被覆盖住?我们把这样一种排列称为棋盘被多米诺牌完美覆盖。这是一个简单的排列问题,同学们能够很快构造出许多不同的完美覆盖。但是,计算不同的完美覆盖的总数就不是一件容易的事情了。不过,同学们 发挥自己的聪明才智,还是有可能做到的。
现在我们通过计算机编程对 3 乘 n 棋盘的不同的完美覆盖的总数进行计算。
在这里插入图片描述
任务
对 3 乘 n 棋盘的不同的完美覆盖的总数进行计算。
输入
一次输入可能包含多行,每一行分别给出不同的 n 值 ( 即 3 乘 n 棋盘的列数 )。当输入 -1 的时候结束。

n 的值最大不超过 30.
输出
针对每一行的 n 值,输出 3 乘 n 棋盘的不同的完美覆盖的总数。
样例输入
2
8
12
-1
样例输出
3
153
2131
思路:有记忆数组的递归。预处理好所有结果直接输出。
对小长方体的组合做简单分析可以得到:2列有三种情况
2n(n > 1)列可以自身成为一整个大的,算上正反就是两种。

#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int memo[32] = {};
int cal(int col){
    if(memo[col])
        return memo[col];
    int tmp = 3 * cal(col - 2);
    int i = 4;
    while(col >= i){
        tmp += 2 * cal(col - i);
        i += 2;
    }
    return tmp;
}
int main(){
    memo[0] = 1;
    memo[2] = 3;
    for(int i = 4; i <= 30; i += 2){
        memo[i] = cal(i);
    }
    while(1){
        int n = 0;
        cin >> n;
        if(n == -1)
            break;
        cout << memo[n] << endl;
    }
    return 0;
}
2.2的幂次方表示

描述
任何一个正整数都可以用2的幂次方表示。例如:

137=27+23+20

同时约定方次用括号来表示,即ab可表示为a(b)。由此可知,137可表示为:

2(7)+2(3)+2(0)

进一步:7=22+2+20(21用2表示)

    3=2+20

所以最后137可表示为:

2(2(2)+2+2(0))+2(2+2(0))+2(0)

又如:

1315=210+28+25+2+1

所以1315最后可表示为:

2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

输入
一个正整数n(n≤20000)。
输出
一行,符合约定的n的0,2表示(在表示中不能有空格)。
样例输入
137
样例输出
2(2(2)+2+2(0))+2(2+2(0))+2(0)
思路:这个层数和括号的安排很重要

#include <iostream>
#include <cmath>
using namespace std;
int n = 0;
void solve(int num, int deep){
    if(num == 0){
        cout << "(0)";
        return;
    }
    else if(num == 1)
        return;
    if(deep != 0)
        cout << "(";
    int n1 = num;
    while(n1 != 0){
        int idx = 0;
        while(pow(2, idx+1) <= n1)
            ++idx;//出现的就是最大的idx
        cout << "2";
        solve(idx, deep + 1);
        n1 -= pow(2, idx);
        if(n1 != 0)
            cout <<'+';
    }
    if(deep != 0){
        cout << ")";
    }
}
int main()
{
    cin >> n;
    solve(n, 0);
    cout << endl;
    return 0;
}
3.扩号匹配问题

描述
在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用" " 标 注 , 不 能 匹 配 的 右 括 号 用 " ? " 标 注 . ∗ ∗ 输 入 ∗ ∗ 输 入 包 括 多 组 数 据 , 每 组 数 据 一 行 , 包 含 一 个 字 符 串 , 只 包 含 左 右 括 号 和 大 小 写 字 母 , 字 符 串 长 度 不 超 过 100 注 意 : c i n . g e t l i n e ( s t r , 100 ) 最 多 只 能 输 入 99 个 字 符 ! ∗ ∗ 输 出 ∗ ∗ 对 每 组 输 出 数 据 , 输 出 两 行 , 第 一 行 包 含 原 始 输 入 字 符 , 第 二 行 由 " "标注,不能匹配的右括号用"?"标注. **输入** 输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100 注意:cin.getline(str,100)最多只能输入99个字符! **输出** 对每组输出数据,输出两行,第一行包含原始输入字符,第二行由" ","?".100cin.getline(str,100)99"","?“和空格组成,”$“和”?"表示与之对应的左括号和右括号不能匹配。
样例输入
((ABCD(x)
)(rttyy())sss)(
样例输出
((ABCD(x)
$$
)(rttyy())sss)(
? ?$
思路:stack的妙用

#include <iostream>
#include <stack>
using namespace std;

char str[110] = {};
char signa[110] = {};
int len = 0;
stack<int> q;

int main()
{
    while(cin.getline(str,101)){
        len = (int)strlen(str);
        for(int i = 0; i < len; ++i)
            signa[i] = ' ';
        for(int i = 0; i < len; ++i){
            switch(str[i]){
                case '(':
                    q.push(i);
                    break;
                case ')':
                    if(!q.empty())
                        q.pop();
                    else
                        signa[i] = '?';
                    break;
            }
        }
        while(!q.empty()){
            signa[q.top()] = '$';
            q.pop();
        }
        cout << str << endl;
        cout << signa << endl;
        memset(str, 0, sizeof(str));
        memset(signa, 0, sizeof(signa));
    }
    return 0;
}

4.42点

描述
42是:

·组合数学上的第5个卡特兰数

·字符’*'的ASCII码

·钼的原子序数

·6与9的乘积结果的13进制表示

·生命、宇宙以及任何事情的终极答案

·以及……表达式(1+5)/2*(6-4)*7的值

因此,小机器人Marvin发明了这个叫42点的小游戏。在这个游戏中,玩家会获得n个数。玩家需要使用’+’、’-’、’*’、’/’、’(’、’)'以及这n个数构成一个合法的中缀表达式,并使得该表达式的值为42。n个数之间的顺序可以改变。表达式运算过程中只能出现整数。

由于过于抑郁,Marvin无力完成这个游戏,于是来找你帮忙。你的任务是对于给定的n个数,判断他们是否能根据上述游戏规则算出42。

输入
第一行为一个数n,1<=n<=6。
第二行为n个数,每个数均为[1,13]范围内的整数。
输出
输出一行,若可以算出42则输出“YES”,否则输出“NO”(注意大小写)。
样例输入
6
1 5 2 6 4 7
样例输出
YES
思路:递归分解成n-1个数凑42,n-2个…

#include <iostream>
#include <string.h>

using namespace std;
int a[7], n;
bool f(int * a,int n)
{
    if(n == 1 && a[1] == 42)
        return true;
    int b[7] = {};
    for(int i = 1;i < n;++i)
        for(int j = i+1;j <= n;++j)
        {
            int p = 1;
            for(int k = 1; k <= n;++k)
            {
                if(k != i && k != j)
                b[p++] = a[k];
            }
            b[p] = a[i] + a[j];
            if(f(b, n-1))
                return true;
            b[p] = a[i] * a[j];
            if(f(b, n-1))
                return true;
            b[p] = a[i] - a[j];
            if(f(b, n-1))
                return true;
            b[p] = a[j] - a[i];
            if(f(b, n-1))
                return true;
            if(a[j] != 0 && a[i] % a[j] == 0)
            {
                b[p] = a[i]/a[j];
                if( f( b, n-1))
                    return true;
            }
            if(a[i] != 0 && a[j] % a[i] == 0)
            {
                b[p] = a[j]/a[i];
                if( f( b,n-1))
                    return true;
            }
        }
    return false;
}
int main()
{
    cin >> n;
    for(int i = 1;i <= n;++i)
        cin >> a[i];
    if(f(a,n))
        cout << "YES";
    else
        cout << "NO";
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值