第 6 章 C++ 过程函数
description
Transcript of 第 6 章 C++ 过程函数
递归用途• 递归程序设计:将一个大问题简化为同样形
式的较小问题。–在一个递归求解中,分解的子问题与最初的问题
具有一样的形式 –作为处理问题的工具,递归技术是一种非常有力
的工具。利用递归不但可以使得书写复杂度降低,而且使程序看上去更加美观
• 递归调用:在一个函数中直接或间接地调用函数本身
典型的递归函数—阶乘函数n!=1*2*3*4*…*(n-1)*n
(n-1)!
递归形式:
其他)!1(*
01!
nn
nn
递归终止条件
long p(int n)
{if (n == 0) return 1;
else return n * p(n-1);
}
简单应用——求 n 的 k 次幂int RaiseIntToPower(int n, int k)
{ if (k == 0) {
return(1);
}
else {
return( n * RaiseIntToPower( n, k - 1));
}
}
Fibonacci 函数0
0
1
1
2
1
3
2
4
3
5
5
6
8
其他)2()1(
11
00
)(
nFnF
n
n
nF
int f(int n){if (n==0) return 0;
elseif (n==1) return 1; else return (f(n-1)+f(n-2));
}
理解递归• 问题:求解 n!
– 可以用循环的方法,即从 1 开始,乘 2 ,再乘 3 ….. 一直乘到 n 。这种方法容易理解,也容易实现
– 由于 n! = n× (n-1)! 数学里定义 0 != 1 ,从而 n! 可以用下面的递归公式表示:
)1()!1(
)1,0(1{!
nnn
nn
Fibonacci 函数的递归实现int f(int n)
{if (n==0) return 0;
elseif (n==1) return 1;
else return (f(n-1)+f(n-2));
}
• 实现效率分析:消费的时间是灾难性的!!!
Fibonacci 函数的迭代实现int f(int n){ int i, fn, fn_1 = 0, fn_2 = 1; if (n == 0) return 0; if (n == 1) return 1; for ( i = 2; i<=n; ++i) { fn = fn_1 + fn_2; fn_2 = fn_1; fn_1 = fn; } return fn;}
消耗的时间:执行 n 次加法和 3n
次赋值!!!
17
代码:• #include<stdio.h>• int f(int n){• return n == 0 ? 1 : f(n-1)*n;• }• int main(){• printf("%d\n", f(3));• return 0;• }
18
递归要调用栈来进行!
皇帝(拥有 main 函数的栈):大臣,你给我算一 下 f(3).
大臣(拥有 f(3) 的栈 ): 知府,你给我算一下 f(2).
知府(拥有 f(2) 的栈):县令,你给我算一下 f(1).
县令(拥有 f(1) 的栈):师爷,你帮我算一下 f(0).
师爷(拥有 f(0) 的栈):回老爷, f(0)=1.
21
追忆 2008 年亚洲哈尔滨赛区• 杨成虎同学的深搜算法就是递归写的,
就是不过,因为该算法在递归调用 5000次就段错误了,后来改成广搜算法(非递归)的就 AC 了,时间多了 1 个小时,离银牌只差 2 名,血的教训!
• 我们要牢记!
解题思路
• 最简单的情况,只有一个盘子:将盘子直接从 A 移到 B
• 大于一个盘子的情况:– 将除了最下面一个盘子外的所有盘子从 A 移
到 C
– 将最下面的盘子从 A 移到 B
– 将 C 上的盘子移回 B
Hanoi 塔函数
void Hanoi(int n, char start, char finish, char temp)
{ if (n==1) cout << start << "->" << finish << '\t';
else { Hanoi(n-1, start, temp, finish);
cout << start << "->" << finish << '\t';
Hanoi(n-1, temp, finish, start);
}
}
34
红与黑 hdu 1312
• 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
35
输入数据• 包括多个数据集合。每个数据集合的第
一行是两个整数 W 和 H ,分别表示 x 方向
• 和 y 方向瓷砖的数量。 W 和 H 都不超过 20 。在接下来的 H 行中,每行包括W 个字符。
• 每个字符表示一块瓷砖的颜色,规则如下:
36
• 1 )‘ .’ :黑色的瓷砖;• 2 )‘ #’ :白色的瓷砖;• 3 )‘ @’ :黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
• 当在一行中读入的是两个零时,表示输入结束。
39
解题思路• 这个题目可以描述成给定一点,计算它所在的连通区
域的面积。需要考虑的问题包括矩阵的大小,以及从某一点出发向上下左右行走时,可能遇到的三种情况:出了矩阵边界、遇到’ .’ 、遇到’ #’ 。
• 设 f(x, y) 为从点 (x,y) 出发能够走过的黑瓷砖总数,则• f(x, y) = 1 + f(x - 1, y) + f(x + 1, y) + f(x, y - 1) + f(x, y +
1)• 这里需要注意,凡是走过的瓷砖不能够被重复走过。
可以通过每走过一块瓷砖就将它作标记的方法保证不重复计算任何瓷砖。
41
• int main(int argc, char *argv[])• {• while(cin>>w>>h)• {• if (w==0&&h==0) break;• • for(int i=1;i<=h;i++)• for(int j=1;j<=w;j++)• cin>>z[i][j]; • for(int i=1;i<=h;i++)• for(int j=1;j<=w;j++)• if (z[i][j]=='@')• cout<<f(i,j)<<endl; • }• //system("PAUSE");• return EXIT_SUCCESS;• }
42
再看递归部分:• int f(int i,int j)• {• if (i<1||i>h||j<1||j>w) // 处理边界• return 0;• if (z[i][j]!='#')• {• z[i][j]=‘#’; // 这句话是啥意思?• return 1+f(i,j-1)+f(i,j+1)+f(i-1,j)+f(i+1,j);• }• else• return 0; • }
23/4/21 45
n=5 时分治法计算斐波那契数的过程。 F(5)
F(4) F(3)
F(3) F(2) F(2) F(1)
F(2) F(1)
F(1) F(0)
F(1) F(0) F(1) F(0)
例:计算斐波那契数:
2)2()1(
11
00
)(
nnFnF
n
n
nF
23/4/21 46
0 1 2 3 4 5 6 7 8 90 1 1 2 3 5 8 13 21 34
动态规划法求解斐波那契数 F(9) 的填表过程 :
注意到,计算 F(n) 是以计算它的两个重叠子问题 F(n-1)和 F(n-2) 的形式来表达的,所以,可以设计一张表填入 n+1
个 F(n) 的值。
23/4/21 47
用动态规划法求解的问题具有特征: 能够分解为相互重叠的若干子问题;
满足最优性原理(也称最优子结构性质):该问题的最优解中也包含着其子问题的最优解。
(用反证法)分析问题是否满足最优性原理:1. 先假设由问题的最优解导出的子问题的解不是最优的 ;2. 然后再证明在这个假设下可构造出比原问题最优解更好
的解,从而导致矛盾。
23/4/21 48
动态规划法设计算法一般分成三个阶段:( 1)分段:将原问题分解为若干个相互重叠的
子问题;( 2)分析:分析问题是否满足最优性原理,找
出动态规划函数的递推式;( 3)求解:利用递推式自底向上计算,实现动
态规划过程。
动态规划法利用问题的最优性原理,以自底向上的方式从子问题的最优解逐步构造出整个问题
的最优解。
23/4/21 49
例 1 斐波那契数列 nefu 85
• 计算斐波那契数列的值!该数列为 1 1 2 3 5 8 13 21 .........
• 有多组数据,每组 1 行,用 N 表示, 1 <= N <= 50 。
• 输出 Fibonacci(N) 的值 !
23/4/21 50
代码:• int n;
• long long feibo[51];
• feibo[1]=1;
• feibo[2]=1;
• for(int i=3;i<=50;i++)
• feibo[i]=feibo[i-1]+feibo[i-2];//打表• while(cin>>n)
• cout<<feibo[n]<<endl;
23/4/21 51
用递归能做吗?• long long data[51];• long long fblq(int n)• {• if (n==1||n==2) return 1;• else• {• if (data[n]==0)• data[n]=fblq(n-1)+fblq(n-2); // 看看这个! 只算 1 次!• return data[n]; • • } • • • }
23/4/21 53
递归的代码:• #include <cstdlib>• #include <iostream>• using namespace std;• long long data[21];• long long jc(int n)• {• if (n==0||n==1) return 1;• if (data[n]==0)• data[n]=n*jc(n-1);• return data[n]; • • }
int main(int argc, char *argv[]) {
int n; memset(data,0,sizeof(data));
while(cin>>n) cout<<jc(n)<<endl; system("PAUSE");
return EXIT_SUCCESS; }
23/4/21 54
递推的代码:• #include <cstdlib>• #include <iostream>• using namespace std;• int main(int argc, char *argv[])• {• int n; long long data[51];• memset(data,0,sizeof(data));• data[0]=1;data[1]=1;• for(int i=2;i<=20;i++)• data[i]=i*data[i-1];• while(cin>>n)• cout<<data[n]<<endl;• system("PAUSE");• return EXIT_SUCCESS;• }
23/4/21 58
递归的代码:• int f[100][100];• int getf(int x,int y)• {• if (f[x][y]!=-1) return f[x][y];• int result;• if (x==0||y==0)• result=1;• else• result=getf(x-1,y)+getf(x,y-1);• f[x][y]=result;• return result;• }
23/4/21 59
Main()• int main(int argc, char *argv[])• {• int i,j,n;• memset(f,-1,sizeof(f));• while(cin>>n)• {• if (n==0) break; • cout<<getf(n,n)<<endl; • }• system("PAUSE");• return EXIT_SUCCESS;• }
23/4/21 60
其实这题递推的代码更短:• int n;• long long data[21][21];• for(int i=0;i<=20;i++)• {• data[i][0]=1;// 处理边界• data[0][i]=1;// 处理边界• }• for(int j=1;j<=20;j++)• for(int k=1;k<=20;k++)• data[j][k]=data[j-1][k]+data[j][k-1];// 公式• while(cin>>n&&n)• cout<<data[n][n]<<endl;