对待递推类的题目,具体问题,具体分析!递推算法首要问题是为了找到相邻数据项的关系——即递推关系。
递推算法避开了求通项公式的麻烦,把一个复杂的问题的求解,分解成了一个若干步连续的简单计算,递推算法也可以看成一种特殊的迭代算法!
【例1】数字三角形。如下所示为一个数字三角形。请编一个程序计算从顶到底的某处的一条路径,使该路径所经过的数字总和最大。只要求输出总和。
1、 一步可沿左斜线向下或右斜线向下走;
2、 三角形行数小于等于100;
3、 三角形中的数字为0,1,…,99;
测试数据通过键盘逐行输入,如上例数据应以如下所示格式输入:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
此题的解法有很多种,我们从递推的思想出发,设想,当从顶层沿某条路径走到第i层向第i+1层前进时,我们的选择肯定是沿其下两条可行路径中最大数字的方向前进,为此,我们可以采用倒推的方法,设a[i][j]存放从i,j出发到达第n层的总和最大。则a[i][j] = max{a[i][j]+a[i+1][j] , a[i][j] + a[i+1][j+1]} 最大总和在a[0][0]处取得.
#include<iostream>
using namespace std ;
int main() {
int n ;
cin >> n ;
int a[100][100] ;
for(int i = 0 ; i < n ; i++ )
for( int j = 0 ; j < i+1 ; j++ )
cin >> a[i][j] ;
for( int ii = n-2 ; ii >= 0 ; ii-- )
for( int jj = 0 ; jj < ii + 1 ; jj++ ) {
if( a[ii+1][jj] > a[ii+1][jj+1] )
a[ii][jj] += a[ii+1][jj] ;
else
a[ii][jj] += a[ii+1][jj+1] ;
}
cout << a[0][0] << endl ;
return 0 ;
}
当n=3时,有如下3种铺法。
编写一个程序,试对给出的任意一个n(n>0), 输出铺法总数。
面对上述问题,如果思考不当,若想获得问题的解答是相当困难的,可以用递推的方法归纳出问题解的一般规律。
当n=1时,只有一种铺法;
当n=2时,有两种铺法;
当n=3时,有三种铺法;
当n=4时,有五种铺法;
由此可以推出一般规律:f(n) = f(n-1)+f(n-2) ;
一般求f(n)时,如果第n个是竖着放,剩下有n-1个需要排列,这时排列方法数为f(n-1);
如果第n个是横着放的,剩下有n-2个需要排列,这时排列方法数为f(n-2);
所以:f(n) = f(n-1)+f(n-2) 就是问题求解的递推公式。
【例3】棋盘格数
设有一个N*M方格的棋盘( l≤ N≤100,1≤M≤100)。求出该棋盘中包含有多少个正方形、多少个长方形(不包括正方形)。
例如:当 N=2, M=3时:
正方形的个数有8个:即边长为1的正方形有6个;边长为2的正方形有2个。
长方形的个数有10个:即2*1的长方形有4个:1*2的长方形有3个:3*1的长方形有2个:3*2的长方形有1个:
程序要求:输入:N,M
输出:正方形的个数与长方形的个数
如上例:输入:2 3
输出:8 10
1、计算正方形的个数:s1
边长为1的正方形个数:n*m ;
边长为2的正方形个数:(n-1)*(m-1)
边长为3的正方形个数:(n-2)*(m-2)
………………
边长为min{n , m}的正方形个数:( n - ( min{n,m} - 1 ) ) * ( m - ( min{n,m} - 1 ) )
根据加法原理得出:
正方形的个数为:边长为1的个数 + 边长为2的个数 + 边长为3的个数 …… + 边长为min{n , m}的个数
2、长方形和正方形的个数之和:s
宽为1的长方形个数和正方形个数之和为:n ;
宽为2的长方形个数和正方形个数之和为:n - 1 ;
宽为3的长方形个数和正方形个数之和为:n - 2 ;
……………………
宽为m的长方形个数和正方形个数之和为:1 ;
长为1的长方形个数和正方形个数之和为:m;
长为2的长方形个数和正方形个数之和为:m - 1 ;
长为3的长方形个数和正方形个数之和为:m - 2 ;
……………………
长为n的长方形个数和正方形个数之和为:1 ;
根据乘法原理:s = (1+2+3+……+n) * (1+2+3+……+m)
3、长方形的个数:s2
s2 = s - s1 ;
#include<iostream>
using namespace std ;
int main() {
int n , m ;
while(cin >> n >> m) {
int s1 = 0 , s , s2 ;
for( int k = 1 ; k <= ( n > m ? m : n ) ; k++ )
s1 += (n-( k -1 )) * ( m-(k-1)) ;
int sum1 = 0 , sum2 = 0 ;
for( int i = 1 ; i <= n ; i++ )
sum1 += i ;
for( int j = 1 ; j <= m ; j++ )
sum2 += j ;
s = sum1 * sum2 ;
s2 = s - s1 ;
cout << s1 << " " << s2 << endl ;
}
return 0 ;
}
【问题描述】
在所有的N位数中,有多少个数中有偶数个数字3?由于结果可能很大,你只需要输出这个答案对12345取余的值。
【输入格式】
读入一个数N
【输出格式】
输出有多少个数中有偶数个数字3。
【输入样例】
2
【输出样例】
73
【数据规模】
1<=N<=1000
【样例说明】
在所有的2位数字,包含0个3的数有72个,包含2个3的数有1个,共73个
考虑这种问题一般从第n-1位推导第n位,且当前位是取偶数个还是奇数个。
可以用a[i][0]表示前i位取偶数个3有几种情况,用a[i][1]表示前i位取奇数个3有几种情况;
则状态转移方程可以表示为:
a[i][0] = a[i-1][0] * 9 + a[i-1][1] ;
a[i][1] = a[i-1][0] + a[i-1][1] * 9 ;
当位数大于1时,第一位不能为0,所以第一位只能有九种取法。
#include<iostream>
#include<stdio.h>
using namespace std ;
int main() {
int n ;
cin >> n ;
int a[100][2] , x ;
a[1][0] = x = 9 ;
a[1][1] = 1 ;
for(int i = 2 ; i <= n ; i++){
if(i == n)
x = 8 ;
a[i][0] = a[i-1][0] * x + a[i-1][1] ;
a[i][1] = a[i-1][1] * x + a[i-1][0] ;
}
cout << a[n][0] << endl ;
return 0 ;
}
【例6】过河卒(Noip2002)
【问题描述】
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。
#include<iostream>
#include<stdio.h>
using namespace std ;
int main() {
int m ,n , x , y ;
cin >> m >> n >> x >> y ;
int dp[100][100] ;
for(int ii = 0 ; ii <= m ; ii++)
for(int jj = 0 ; jj <= n ; jj++)
dp[ii][jj] = 1 ;
dp[0][0] = 1 ;
dp[x][y] = 0 ;
dp[x+1][y+2] = 0 ;
dp[x+1][y-2] = 0 ;
dp[x+2][y+1] = 0 ;
dp[x+2][y-1] = 0 ;
dp[x-1][y+2] = 0 ;
dp[x-1][y-2] = 0 ;
dp[x-2][y+1] = 0 ;
dp[x-2][y-1] = 0 ;
for(int i = 0 ; i <= m ; i++ )
for(int j = 0 ; j <= n ; j++) {
if(!dp[i][j]) continue ;
if(!i&&!j) continue ;
else if(i == 0) dp[i][j] = dp[i][j-1] ;
else if(j== 0) dp[i][j] = dp[i-1][j] ;
else
dp[i][j] = dp[i-1][j] + dp[i][j-1] ;
}
cout << dp[m][n] << endl ;
return 0 ;
}