期末复习之递归
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个字符! **输出** 对每组输出数据,输出两行,第一行包含原始输入字符,第二行由"
"标注,不能匹配的右括号用"?"标注.∗∗输入∗∗输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100注意:cin.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";
}