递推
HDU 2044 "一只小蜜蜂"
•题解
类似与斐波那契数列;
定义 $f[i]$ 表示从 $1$ 号蜂房走到 $i$ 号蜂房的总方案数,那么有 $f[1]=f[2]=1$,$f[i]=f[i-1]+f[i-2]\ ,\ i > 2$;
但此题要求你从 $a$ 号蜂房走到 $b$ 号蜂房的总方案数;
那么,你就需要将 $a$ 号蜂房设置为 $1$ 号蜂房即可,即令 $f[a]=f[a+1]=1$,$f[i]=f[i-1]+f[i-2]\ ,\ i > a+1$;
输出 $f[b]$ 即可;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 5 int a,b; 6 ll f[100]; 7 8 int main() 9 { 10 int T; 11 scanf("%d",&T); 12 while(T--) 13 { 14 scanf("%d%d",&a,&b); 15 f[a]=1; 16 f[a+1]=1; 17 for(int i=a+2;i <= b;++i) 18 f[i]=f[i-1]+f[i-2]; 19 20 printf("%lld\n",f[b]); 21 } 22 return 0; 23 }
•变形
题面不变,数据加强;
令 $0<a<b\le 100000$,并要求最终答案对 $1e9+7$ 取模;
•分析
对变形前代码时间复杂度分析的话,最坏的情况是,$O(T\cdot 50)$,当然,如果 $T < 10^6$ 还是可以过得;
但是,加大数据后,这可就不一定了;
考虑到每次测试数据中的 $for$ 循环,可不可以提前预处理出来;
如果 $a=1$ ,那么,斐波那契数列第 $b$ 项 $f[b]$ 就是答案;
如果 $a \noe 1$ 呢?
我们考虑一下比之前多计算的情况;
首先,在之前的代码中,每次都令 $g[a]=g[a+1]=1$;(为不与斐波那契 $f$ 混淆,以下将上题中的 $f$ 改为 $g$)
也就是说,$f[a]$ 比 $g[a]$ 多计算了 $f[a]-1$ 中情况,同样,$f[a+1]$ 比 $g[a]$ 多计算了 $f[a+1]-1$ 中情况;
定义 $x=f[a]-1\ ,\ y=f[a+1]-1$,那么,由递推式 $g[a+2]=g[a+1]+g[a]$ 可得 $f[a+2]$ 比 $g[a+2]$ 多计算了 $x+y$;
也就是说,$f[b]$ 比 $g[b]$ 多计算的东西与 $a,b$ 之间的距离有关;
定义 $X[i]\ ,\ Y[i]$ 分别表示从 $a$ 蜂房到 $b$ 蜂房多计算了 $X[i]$ 个 $x$ 和 $Y[i]$ 个 $y$ 值;
那么,只需要从 $f[b]$ 中减掉就行了;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int N=1e5+50; 5 const int MOD=1e9+7; 6 7 int a,b; 8 ll x[N]; 9 ll y[N]; 10 ll f[N]; 11 12 void Init() 13 { 14 x[0]=y[1]=1; 15 y[0]=x[1]=0; 16 for(int i=2;i < N;++i) 17 { 18 x[i]=x[i-1]+x[i-2]; 19 y[i]=y[i-1]+y[i-2]; 20 x[i] %= MOD; 21 y[i] %= MOD; 22 } 23 f[1]=f[2]=1; 24 for(int i=3;i < N;++i) 25 f[i]=(f[i-1]+f[i-2])%MOD; 26 } 27 int main() 28 { 29 Init(); 30 31 int T; 32 scanf("%d",&T); 33 while(T--) 34 { 35 scanf("%d%d",&a,&b); 36 ll u=f[a]-1; 37 ll v=f[a+1]-1; 38 ll ans=f[b]-(u*x[b-a]%MOD+v*y[b-a]%MOD)%MOD; 39 ans=(ans+MOD)%MOD; 40 41 printf("%lld\n",ans); 42 } 43 44 return 0; 45 }
HDU 2045
•题意
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green) 三色涂每个格子;
每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色;
求全部的满足要求的涂法;
•题解(递推)
定义 $f[i]$ 表示 $i$ 个格子的总方案数;
假设 $f[1],f[2],\cdots ,f[i-1]$ 已经求出,如何求出 $f[i]$ 呢?
考虑到这一点 $i$ 与 $i-1$ 不同色,同时 $i$ 与 $1$ 也不同色;
$i$ 个格子的总方案数是不是可以分为以下两种:
① 第 $i-1$ 个格子与第 $1$ 个格子不同色
② 第 $i-1$ 个格子与第 $1$ 个格子同色
对于情况①,已知 $f[i-1]$ 求解的是 $i-1$ 与 $1$ 不同色的总方案数,所以,情况①的答案为 $f[i-1]$;
对于情况②,因为第 $i-1$ 个格子与第 $1$ 个格子同色,且第 $i-1$ 个格子与第 $i-2$ 个格子不同色;
也就是说,求解第 $i-1$ 个格子与第 $1$ 个格子同色的总方案数可以转化为求解第 $i-2$ 个格子与第 $1$ 个格子不同色的总方案数;
易得 $f[i-2]$ 就是解,又因为第 $i-1$ 个格子与第 $1$ 个格子同色,所以,第 $i$ 个格子有两种选择;
所以情况②的总方案数为 $2\times f[i-2]$;
所以,$f[i]=f[i-1]+2\times f[i-2]$;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int N=60; 5 6 int n; 7 ll f[N]; 8 9 int main() 10 { 11 f[1]=3; 12 f[2]=f[3]=6; 13 for(int i=4;i < N;++i) 14 f[i]=f[i-1]+2*f[i-2]; 15 16 while(~scanf("%d",&n)) 17 printf("%lld\n",f[n]); 18 }
HDU 2049 "不容易系列之(4)——考新郎"
•题解
定义 $f[i][j]$ 表示 $i$ 对新婚夫妇中,$j$ 个新郎找错新娘的总方案数;
假设 $i-1$ 对新婚夫妇的所有新郎找错新娘的总方案数已经求出;
那么,如果现在又来了一对新婚夫妇,如何求解这 $i$ 对新婚夫妇中所有新郎找错新娘的总方案数呢?
以求解 $f[i][j]$ 为例;
考虑到以下几种情况:
- 前 $i-1$ 对新婚夫妇中有 $j$ 个新郎找不到新娘,那么,第 $i$ 个新郎一定找对了新娘
- 前 $i-1$ 对新婚夫妇中有 $j-1$ 个新郎找不到新娘,那么,第 $i$ 个新郎势必要与前 $i-1$ 对新婚夫妇中找不到新娘的新郎互换新娘才满足 $j$ 个新郎找不到新娘的条件
- 前 $i-1$ 对新婚夫妇中有 $j-2$ 个新郎找不到新娘,那么,第 $i$ 个新郎势必要与前 $i-1$ 对新婚夫妇中找到新娘的新郎互换新娘才满足 $j$ 个新郎找不到新娘的条件
- 情况 1 的总方案数为 $f[i-1][j]$
- 情况 2 的总方案数为 $f[i-1][j-1]\cdot (j-1)$,第 $i$ 个新郎与这 $j-1$ 个找不到新娘的新郎中的任意一个新郎互换新娘都可以满足条件
- 情况 3 的总方案数为 $f[i-1][j-2]\cdot (i-j+1)$,第 $i$ 个新郎与找到新娘的新郎中的任意一个互换新娘都可以满足条件
综上,$f[i][j]=f[i-1][j]+f[i-1][j-1]\cdot (j-1)+f[i-1][j-2]\cdot (i-j+1)$ ;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int N=21; 5 6 int n,m; 7 ll f[N][N]; 8 9 void Init() 10 { 11 for(int i=1;i < N;++i) 12 f[i][0]=1; 13 for(int i=2;i < N;++i) 14 for(int j=2;j <= i;++j) 15 f[i][j]=f[i-1][j]+(j-1)*f[i-1][j-1]+(i+1-j)*f[i-1][j-2]; 16 } 17 int main() 18 { 19 Init(); 20 int T; 21 scanf("%d",&T); 22 while(T--) 23 { 24 scanf("%d%d",&n,&m); 25 printf("%lld\n",f[n][m]); 26 } 27 }