ACM入门教程-线性递推

写在前面

今天讲一个基本的算法思想递推,所谓递推就是根据当前值能够求出下一个值,比如我们熟悉的斐波那契数列,它规定了前两个数为1,剩下的数由f(n)=f(n-1)+f(n-2)来推导而出,在算法竞赛中,常常抛出一个实际问题,大家需要抽象出数学模型也就是递推式子就能顺利解题目。

奇怪的数列

Problem Description

塔兹米是一个喜欢数学的好孩子。有一天,他发现了一个奇怪的数列:
1,1,2,3,5,8,……
在思考许久之后,聪明如他也没办法算出数列中第N个数字,你能帮助他吗?

Input

有多组测试数据。
每组测试数据有一个整数N(0

Output

对于每个测试数据,输出数列中第N个数是多少。
每个结果占一行。

SampleInput

1
5

SampleOutput

1
5

思路分析

观察得到题目中的数列为斐波那契数列,数据范围很小,先预处理一遍然后输出就好啦

ACcode

#include 
#include 
using namespace std;

int main(){
    int n;
    int a[43] = {0,1,1};
    for(int i = 3; i <= 42; i++)
        a[i] = a[i-1]+a[i-2];
    while(cin>>n && n){
        cout<<a[n]<<endl;
    }
    return 0;
}

奇怪的台阶

Problem Description

QAQ在一个楼梯上,QAQ现在位于第0阶,无聊的QAQ发现,以他腿的长度,每次可以上1阶,2阶,3阶,而这个台阶一共有N阶,那么QAQ走到第N阶共有多少种走法?

Input

多组测试数据,每组数据一行,包含一个数字N。(1 <= N <= 30)

Output

对于每组数据:
输出到达第N阶共有多少种走法

SampleInput

1
2

SampleOutput

1
2

思路分析

这道题相对于上一题就有点让人摸不着头脑了,首先我们很容易得到 在0阶时,到达1阶有1种办法,到达2阶有2种办法,到达3阶有4种办法(0->1->2->3)(0->2->3)(0->1->3)(0->3).其实那么每一阶当中有什么规律呢?我们规定f[n]为到达第n阶的方案,我们考虑对于当前状态的上一步状态比如1可以由0一步到达,显然f[1]=f[0+1],而f[2]=f[0+2]+f[1+1],f[3]=f[0+3]+f[1+2]+f[2+1] f[4]=f[1+3]+f[2+2]+f[3+1]…根据状态的转移我们可以得到f[1]=1,f[2]=2,f[3]=4,f[n]=f[n-1]+f[n-2]+f[n-3] (n>3),从题目本身来说,就是考虑当前阶数的可以分别由+1,+2,+3三个操作到达。

ACcode

#include 
int main()
{
	int n,i[34]={1,2,4},j,m;
	for(j=3;j<34;j++)
		i[j]=i[j-1]+i[j-2]+i[j-3];
	while(~scanf("%d",&n))
		printf("%d\n",i[n-1]);
	return 0;
}

我读书少,你们得帮帮我

Problem Description

这是一题简单的题目,考的只是你的数学而已。
我一直都很好奇愚公一家到底有多少人。好吧,CJP说你们会帮我的。
假设愚公家族 每个人的一生是这样度过的:(当他回首往事的时候。。。开个玩笑,请无视) 头 20 年用来生长发育以及挖山,第21年(可以理解为21岁的时候)开始 每年生下一个孩子( 自交,任性, 没妻子, 全生男,且 不考虑死亡),当然还要去挖山。
我们默认愚公1岁的时候为第一年(第21年愚公生下第一胎),求第 N年愚公家族(愚公家族不需要妻子,别考虑太多)有多少人。

Input

有多组测试数据,每组占一行,包括一个数N(0

Output

对于每组测试,输出整数M,M为愚公家族的人数。

SampleInput

1
21
41

SampleOutput

1
2
23

思路分析

同样规定状态f[n]为第n天的人口,那么这一题有怎么样的一个状态转移关系呢?我们看到题目中的一个关键数字20,且从21年以后人数开始发生变化,对于第n+1年,f[n+1]=f[n]+(新增人口),我们考虑一下增加的人口就可以了,那么会增加多少人呢?对于第n+1年来说,前20年的人都具备生育能力了,那显然他们都要生也就是他们的数量翻倍就好,所以得到的式子就是 f[n]=f[n-1]+f[n-20](n>20)

ACcode

#include 
int main()
{
	int c[70];
	int n, i;
	while (scanf("%d", &n) != EOF)
	{
		for (i = 1; i <= n; i++)
		{
			if (i <= 20)
				c[i] = 1;
			else
			    c[i] = c[i - 1] + c[i - 20];
		}
		printf("%d\n", c[n]);
	}
}

骨牌铺方格

Problem Description

在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数.
例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:
ACM入门教程-线性递推_第1张图片

Input

输入数据由多行组成,每行包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0

Output

对于每个测试实例,请输出铺放方案的总数,每个实例的输出占一行。

SampleInput

1
3
2

SampleOutput

1
3
2

思路分析

相信通过上面几题的分析,大家都知道第一步要干什么了,首先还是状态的确定,f[n]为2*n大小矩阵的摆放方案数,再考虑能够一步到达n的状态。通过横着摆两个或者竖着摆一个可以到达当前填充好的矩阵。所以惊人的发现这居然是个斐波那契数列的变种,f[n]=f[n-1]+f[n-2];

ACcode

#include
using namespace std;
int main()
{
   long long int a[55];
    a[1]=1;
    a[2]=2;
    for(int i=3;i<=50;i++)
        a[i]=a[i-1]+a[i-2];
    int n;
    while (cin>>n)
    {
        cout<<a[n]<<endl;
    }
    return 0;
}

杨辉三角

Problem Description

还记得中学时候学过的杨辉三角吗?具体的定义这里不再描述,你可以参考以下的图形:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1

Input

输入数据包含多个测试实例,每个测试实例的输入只包含一个正整数n(1<=n<=30),表示将要输出的杨辉三角的层数。

Output

对应于每一个输入,请输出相应层数的杨辉三角,每一层的整数之间用一个空格隔开,每一个杨辉三角后面加一个空行。

SampleInput

2 3

SampleOutput

1
1 1

1
1 1
1 2 1

思路分析

本题根据杨辉三角的定义出发即可f[i][j]=f[i-1][j]+f[i-1][j-1],注意一下边界条件,不过多赘述。

ACcode

#include
int main()
{
	int a, b, c;
	int x[50][50];
	while (~scanf("%d", &a))
	{
		for (b = 1; b < 50; b++)
		{
			x[b][1] = 1;
			x[b][b] = 1;
		}
		for (c = 3; c <= a; c++)
			for (b = 2; b < c; b++)
			{
				x[c][b] = x[c - 1][b] + x[c - 1][b - 1];
			}
		for (c = 1; c <= a; c++)
		{
			for (b = 1; b < c; b++)
				printf("%d ", x[c][b]);
			printf("%d\n", x[c][b]);
		}
		printf("\n");
	}
	return 0;
}

武士之魂2:不是假发是桂!

Problem Description

在这天人当道的时代,却依然有着那么一些武士为了推翻昏庸的政府而不懈努力着。桂正是其中之一。但是他却被真选组的人小看了,他们还用“假发”这个外号称呼他以此来激怒他。
“不是假发,是桂!”桂愤怒的大喊。终于他决定给不可一世的真选组一点教训。他制定了一个完美的作战方案——潜入真选组,把他们所有厕所的厕纸全部翻转放置!从而让他们陷入抽不完的厕纸地狱!对于真选组来说,你可以用刀切断厕纸让厕纸可以取出!但是要用刀切断厕纸你身为剑士的尊严就该遭到侮辱!但如果没擦干净你也会被人鄙视!这样一个两难的抉择会打击真选组的积极性!这样兵不血刃的方法也只有桂可以想出来吧!真不愧是被称为狂乱贵公子的男人!
但是真选组的防守也是很严密的,桂必须抓紧时间行动,真选组的本部的地形可以看做多个矩形组成左下角的点为入口,右上角的点为出口,矩形的边即为路径,但是桂并不知道真选组具体有多大,为了能安全的走到出口,桂将只会沿着路向上或者向右走,并翻转沿路厕所里的厕纸。这样一来,桂就有很多的路线可以选择,他是一个追求完美的人,他想知道他会因此少翻转多少的厕纸,所以他必须要算出总共有多少可选路线,但是面对即将到来的潜入,由于这个数可能会很大,因此他已无暇去想了,这就只能由你来帮他计算了。

Input

首先输入一个整数 N ,代表接下来有 N 组测试样例,接下来 N 行输入一个整数 M ,代表真选组的地形为 M * M 的矩形。 1<=M<=10 。

Output

输出有多少条可选的路线。

SampleInput

2
1
2

SampleOutput

2
6

思路分析

告诉你起点(1,1)每次可以向右走或者向下走问你到达(n,m)的方案数,首先还是规定状态f[i][j]为到达(i,j)的方案,再考虑能一步到达(i,j)的情况即为(i,j-1)(向右走) (i-1,j)(向下走),很明显能一步到达(i,j)的状态,(i,j)也能一步回去,题目给我们的是向右和向下,所以我们只要在当前格向左或者向上就可以找到对应的状态,所以本题状态转移方程为f[i][[j]=f[i-1][j]+f[i][j-1];

ACcode

# include
int main()
{
	int dp[11][11],i,j;
	int t, n;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d", &n);
		for (i = 1; i <= n; i++)
		{
			dp[i][0] = 1;
			dp[0][i] = 1;
		}
		for (i = 1; i <= n; i++)
		{
			for (j = 1; j <= n; j++)
			{
				dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
		}
			printf("%d\n", dp[n ][n]);
		
	}
	return 0;

}

总结

大家可以发现,所谓递推题,就是需要找到一个方程来转移状态,那么我们首先的步骤就是用数组表达状态,然后处理一下边界条件,也就是最开始的起点,然后接下来所有状态根据题目来考虑,当前状态的所有上一个状态,累加即可,这一思路其实就是动态规划的思想,希望大家好好思考这些问题,随着题目的复杂化,这些状态转移可能会难以看出,或者很难转移,我们又会学习更新的办法来解决。

你可能感兴趣的:(ACM教程,算法,c++,矩阵)