Ural 1225. Flags 斐波那契DP
1225. Flags
Time limit: 1.0 second
Memory limit: 64 MB
Memory limit: 64 MB
On the Day of the Flag of Russia a shop-owner decided to decorate the show-window of his shop with textile stripes of white, blue and red colors. He wants to satisfy the following conditions:
- Stripes of the same color cannot be placed next to each other.
- A blue stripe must always be placed between a white and a red or between a red and a white one.
Determine the number of the ways to fulfill his wish.
Example. For N = 3 result is following:
Input
N, the number of the stripes, 1 ≤ N ≤ 45.
Output
M, the number of the ways to decorate the shop-window.
Sample
input | output |
---|---|
3 |
4 |
Problem Source: 2002-2003 ACM Central Region of Russia Quarterfinal Programming Contest, Rybinsk, October 2002
最近被问到了这个问题,其实是一个很简单的DP,但就有人纠结为什么就变成了斐波那契。
首先说下DP的思路:有三种状态,白、蓝、红,直接对应到0、1、2吧,于是可以定义一个数组dp[46][3],因为蓝的只能在白和红之间,所以只有一格的时候初始状态为:dp[1][0]=dp[1][2]=1,dp[1][1]=0。
对于接下来的每格,这一格是红色依赖于前一格不是红色,这一格是白色依赖于前一格不是白色;另外假设如果前一格是蓝色,那么这一格是红/白色就依赖于前面第二格不是红/白色,于是有下面的递推:
白色:dp[i][0]=dp[i-1][2]+dp[i-2][2];
蓝色:dp[i][1]=dp[i-1][0]+dp[i-1][2];
红色:dp[i][2]=dp[i-1][0]+dp[i-2][0];
最后把dp[N][0]和dp[N][2]加起来就是所有情况的总和了,因为最后一格无论如何也不可能是蓝色的。
1 int main2() { 2 int N;long long dp[46][3]={0}; 3 dp[1][0]=dp[1][2]=1; 4 scanf("%d", &N); 5 for(int i=2; i<=N; i++) 6 dp[i][0]=dp[i-2][2]+dp[i-1][2], 7 dp[i][1]=dp[i-1][0]+dp[i-1][2], 8 dp[i][2]=dp[i-2][0]+dp[i-1][0]; 9 printf("%lld\n",dp[N][0]+dp[N][2]); 10 return 0; 11 }
然后我们可以发现一些有趣的事情,其实白色和红色的递推是相互依赖的,而蓝色根本不会有什么用,因为这一格是蓝色取决于前一格不是蓝色,即前一格是白色或红色的情况总和,这个数量并不能为下一格提供对前两格的有效判断。
仔细观察发现,原来白色和红色就是两个相同的斐波那契数列,这样就好办了,两个合成一个,f[1]=f[2]=2,f[i]=f[i-1]+f[i-2],最后f[N]就是N格的情况总和。
1 int main() { 2 int N;long long dp[46]={0,2,2}; 3 scanf("%d", &N); 4 for(int i=3; i<=N; i++) 5 dp[i]=dp[i-1]+dp[i-2]; 6 printf("%lld\n",dp[N]); 7 return 0; 8 }
最后,一眼看出是斐波那契是如何做到的呢?首先无视蓝色,第一个格子f[1]=2,只有白/红两种情况,因为白/红不能连续出现,所以这一格是什么,已经定死了下一格是什么,于是第i个格子f[i]=f[i-1]。然后看看加入蓝色会发生什么:如果前一格是蓝色,那么当前格子一定是和前两格不同的颜色,则f[i]=f[i-2];综合考虑下,f[i]=f[i-1]+f[i-2]。