9273:PKU2506Tiling(2种解法递归和递推)

9273:PKU2506Tiling

总时间限制:

2000ms

单个测试点时间限制:

1000ms

内存限制:

131072kB

描述

对于一个2行N列的走道。现在用1*2,2*2的砖去铺满。问有多少种不同的方式。

下图是一个2行17列的走道的某种铺法。



输入

整个测试有多组数据,请做到文件底结束。每行给出一个数字N,0 <= n <= 250

输出

如题

样例输入

2 
8 
12 
100 
200

样例输出

3 
171 
2731 
845100400152152934331135470251
1071292029505993517027974728227441735014801995855195223534251

先去发现规律:

我们发现如果第n个是 一个 1 * 2 砖 竖着放  就是f(n - 1)种情况

如果第n 个是  1 * 2的砖横放就有 f(n-2)种情况

如果第n个是 2*2的砖也是 有f(n-2)种情况

然后递归方程和状态方程就出来了

f(n) = f(n-1) + 2 * f(n-2)

但是这个题的数很大我们用long long 也存不下,所以就需要大数存储,用到大数相加大数相乘。

然后写一下解题步骤

1)我们先确定状态方程(递归方程);

2)递归边界

3)运用大数相加

4)运用大数乘低精度

递归写法

#include<iostream>
#include<cstring>
using namespace std;
int dp[260][305];

int* Plus (int a[], int b[])
{
	int i, k;
	k = a[0] > b[0] ? a[0] : b[0];
	for( i = 1;i <= k; i++)
	{
		a[i+1] += (a[i] + b[i]) / 10;
		a[i] = (a[i] + b[i]) % 10; 	
	}	
	if(a[k+1] > 0)
	a[0] = k+1;
	else
	a[0] = k;
	return a;
}
int* Multi(int a[],int key)//高精度 乘以低精度 a= a*key 
{
	int i,k;
	if( key == 0)
	{
		memset(a, 0,sizeof(a));
		a[0] = 1;
		return 0;
	}
	for( i = 1;i <= a[0];i++)
		a[i] = a[i] * key;
	for( i = 1;i <= a[0]; i++)
	{
		a[i+1] += a[i]/10 ;
		a[i] %= 10;
	}
	while(a[a[0] + 1] > 0) a[0] ++ ;
	return a;
}
void give(int a[], int b[]){
	for(int i = 0; i <= a[0]; i++){
		b[i] = a[i];
	}
}

int* DFS(int i){
	if(dp[i][0] != 0)
		return dp[i];
	else{
		int temp_1[305] = {0};
		int temp_2[305] = {0};
		give(DFS(i - 1), temp_1);
		give(DFS(i - 2), temp_2);
		give(Plus(temp_1, Multi(temp_2, 2)), dp[i]); 
		return dp[i];
	}
}

int main(){
	memset(dp, 0, sizeof(dp));
	int n;
	dp[0][1] = 1;
	dp[0][0] = 1;
	dp[1][1] = 1;
	dp[1][0] = 1;
	dp[2][0] = 1;
	dp[2][1] = 3;
	DFS(255);
	while(cin >> n){
		for(int i = dp[n][0]; i >= 1; i--)
			cout << dp[n][i];
		cout << endl;
	}
	
	return 0;
}

非递归写法

状态方程dp从下往上推一波

#include<iostream>
#include<cstring>
using namespace std;
int dp[260][305];

int main(){
	memset(dp, 0, sizeof(dp));
	int n;
	dp[0][1] = 1;
	dp[0][0] = 1;
	dp[1][1] = 1;
	dp[1][0] = 1;
	dp[2][0] = 1;
	dp[2][1] = 3;
	for(int i = 3; i <= 255; i++){
		int k = max(dp[i-1][0], dp[i-2][0]);
		for(int j = 1; j <= k; j++){
			dp[i][j] = dp[i-1][j] + dp[i-2][j] * 2;
		}
		dp[i][0] = k;
		for(int e = 1; e <= k; e++){
			int temp = dp[i][e] / 10;
			dp[i][e] %= 10;
			dp[i][e + 1] += temp;
		}
		if(dp[i][k + 1] != 0)
			dp[i][0] = k + 1;
	}
	while(cin >> n){
		for(int i = dp[n][0]; i >= 1; i--)
			cout << dp[n][i];
		cout << endl;
	}
	
	return 0;
}

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值