yukicoder No.533
問題文
1段, 2段, 3段登ることができる人がn段の階段を登っていく場合、n段目までの方法の総数を出力する問題です。 しかし、1つ条件があり、連続して同じ段数を登ることはできません。
解説
基本的な解法の方針は動的計画法です。
連続して、同じ段数登ることができないので何段登って到達したかを保持するために配列を3つ用意します。
dp[i][1] := i段目に1段登って到達する場合の数
dp[i][2] := i段目に2段登って到達する場合の数
dp[i][3] := i段目に3段登って到達する場合の数
ここで、例えば1段のぼって到達した場合にはその前は2段か3段でなければなりせん.よって以下の漸化式を立てることができます。
dp[i][1] = dp[i-1][2] + dp[i-1][3]
dp[i][2] = dp[i-2][1] + dp[i-2][3];
dp[i][3] = dp[i-3][1] + dp[i-3][2];
これをループを回して計算させればdp[n][1]+dp[n][2]+dp[n][3]が答えになります。
しかし、これではnが106であるためメモリが足りません。
そのため前三段分を配列に保持してそれらを用いて計算させるようにすれば答えを求めることができます。
コード
#include <iostream> #include <queue> using namespace std; const int mod = 1e9+7; int main(void){ int n; long long dp[100000][4]; queue<long long> memo[4]; cin >> n; if(n==1){ cout << "1" << endl; return 0; } if(n == 2){ cout << "1" << endl; return 0; } memo[1].push(1); memo[2].push(0); memo[3].push(0); memo[1].push(0); memo[2].push(1); memo[3].push(0); memo[1].push(1); memo[2].push(1); memo[3].push(1); long long m[5][4]; for(int i=4; i<=n; i++){ m[1][1] = memo[1].front(); m[1][2] = memo[2].front(); m[1][3] = memo[3].front(); memo[1].pop(); memo[2].pop(); memo[3].pop(); m[2][1] = memo[1].front(); m[2][2] = memo[2].front(); m[2][3] = memo[3].front(); memo[1].pop(); memo[2].pop(); memo[3].pop(); m[3][1] = memo[1].front(); m[3][2] = memo[2].front(); m[3][3] = memo[3].front(); memo[1].pop(); memo[2].pop(); memo[3].pop(); m[4][3] = (m[1][2] + m[1][1])%mod; m[4][2] = (m[2][1] + m[2][3])%mod; m[4][1] = (m[3][2] + m[3][3])%mod; memo[1].push(m[2][1]); memo[2].push(m[2][2]); memo[3].push(m[2][3]); memo[1].push(m[3][1]); memo[2].push(m[3][2]); memo[3].push(m[3][3]); memo[1].push(m[4][1]); memo[2].push(m[4][2]); memo[3].push(m[4][3]); } memo[1].pop(); memo[2].pop(); memo[3].pop(); memo[1].pop(); memo[2].pop(); memo[3].pop(); cout << (memo[1].front()+memo[2].front()+memo[3].front())%mod << endl; return 0; }