1. 程式人生 > 其它 >P1255 數樓梯 題解

P1255 數樓梯 題解

題目描述

樓梯有 \(N\) 階,上樓可以一步上一階,也可以一步上二階。

編一個程式,計算共有多少種不同的走法。

輸入格式

一個數字,樓梯數。

輸出格式

輸出走的方式總數。


解決

1. 遞迴的方法

\(1\) 階樓梯時,輸出 1 ;有 \(2\) 階時,輸出 2 ;否則,遞迴 pa(n-2)+pa(n-1)

這種方法十分好理解,但不能 AC 。

程式碼:

#include<bits/stdc++.h>
using namespace std;
int n;
long long pa(int n){
	if(n==1){
		return 1;
	}else if(n==2){
		return 2;
	}else{
		return pa(n-1)+pa(n-2);
	}
}
int main(){
	cin>>n;
	cout<<pa(n);
}

這種方法可以得到 \(50\) 分:

上圖中遞推造成 TLE,是因為函式一遍又一遍地呼叫自身,耗費了大量時間。

那麼有沒有耗時較少的方法呢?我們不難想到遞推。

2. 純遞推的方法

我們試著列舉各種階數和對應的走法數:

比如,在 \(N=1\) 時,只有一種走法:上 \(1\) 階。

\(N=2\) 時,有兩種走法:11 和 2 。

\(N=3\) 時,有三種走法:111、12 和 21 。

列舉更多的數字,得到下表:

階數 \(N\) 走法數
1 1
2 2
3 3
4 5
5 8
6 13
7 21

這個表格中的走法數神似斐波那契數列,只是第二項變成了 \(2\)

,由此寫出:

  • s[1]=1
  • s[2]=2
  • s[n]=s[n-1]+s[n-2]\(n>2\)

由此得到程式碼:

#include<bits/stdc++.h>
using namespace std;
long long go[1010]={1,2};
int n;
int main(){
	cin>>n;
	for(int i=2;i<n;i++){
		go[i]=go[i-1]+go[i-2];
	}
	cout<<go[n-1];
	return 0;
} 

可是這種方法依然不能 AC:

出現了兩個 WA 和 RE,為什麼?我們嘗試輸入本題的資料範圍 \(5000\)

,輸出了:

-8190626429588521950

這顯然是得出的走法數超出了 long long 的範圍。所以還得用高精度。

3. 遞推+高精度的方法

利用高精度加法即可。

直接上程式碼:

#include<bits/stdc++.h>
using namespace std;
int n; // 樓梯階數
int a[5002][5002]; // 能過即可,不要開太大

int l=1; // l 標記長度
int g_sum(int c){ // 負責高精度加法
	for(int i=1;i<=l;i++){ // 兩數按位相加(這裡必須用 <=1 ,否則 20 分)
		a[c][i]=a[c-1][i]+a[c-2][i];
	}
	for(int i=1;i<=l;i++){
		if(a[c][i]>9){ // 如果某一位上兩位數
			a[c][i+1]+=a[c][i]/10; // 模擬進位
			a[c][i]=a[c][i]%10;
			
		}
		if(a[c][l+1]){ // 擴充容量
			l++;
		}
	}
}
int main(){
	a[1][1]=1; // 初始化 (等同於go[1]=1)
	a[2][1]=2; // 等同於go[2]=2
	cin>>n;
	for(int i=3;i<=n;i++){
		g_sum(i); // 相加即可
	}
	for(int i=l;i>0;i--){
		cout<<a[n][i]; // 倒著輸出結果
	}
	return 0;
}

今天,你 AC 了嗎?


附上做題過程(慘不忍睹):