第1章 递推算法
目录:
A.错排问题
B.奇怪汉诺塔
C.数的划分
D.传球游戏
E.平铺方案
A.【例题一】错排问题
分析
考虑第n 个元素 放在k 位上 那么有(n−1)种方案( k ≠ n )
再考虑这个k 当它在n 位时 就有由于n,k两元素位置相同 其他n−2个元素错排即可
当k不在n位时 直接n−1个元素错排
递推式:
f
n
=
(
n
−
1
)
∗
(
f
n
−
1
+
f
n
−
2
)
f_n=(n-1)*(f_{n-1}+f_{n-2})
fn=(n−1)∗(fn−1+fn−2)
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n,f[100001];
int main()
{
cin>>n;
f[1]=0;f[2]=1;
for(int i=3;i<=n;i++)
{
f[i]=(i-1)*(f[i-1]+f[i-2]);
}
cout<<f[n];
return 0;
}
B. 【例题2】奇怪汉诺塔
分析
考虑只有3 座塔的汉诺塔问题 设fn
为n 盘子3 塔的最优步数
先把n−1个盘子移到B塔 则步数为
f
[
n
−
1
]
f[n − 1]
f[n−1]
再把A 塔上1 个盘子移到C 塔 步数为1
最后把B 塔上n−1盘子移到C塔 步数为
f
[
n
−
1
]
f[n − 1]
f[n−1]
最终递推式:
2
∗
f
n
−
1
+
1
2*f_{n-1}+1
2∗fn−1+1
那么这道题就可以转化过来
f n表示n 盘子4 塔的最优步数 先将j个盘子从A 移到B 步数为f j
将剩下n−j个盘子移到D
剩余三塔 就可以用上面的递推式预处理出这个
f
[
n
−
j
]
f[n − j]
f[n−j]
最后将B 的j 个盘子移到D 步数为
f
[
j
]
f[j]
f[j]
再把它们加起来取min即可.
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int d[100001],f[100001];
int main()
{
d[1]=1;
for(int i=2;i<=12;i++)
{
d[i]=2*d[i-1]+1;
}
f[1]=1;
for(int i=2;i<=12;i++)
{
f[i]=2147483647;
for(int j=1;j<=i;j++)
{
f[i]=min(f[i],2*f[j]+d[i-j]);
}
}
for(int i=1;i<=12;i++)
{
cout<<f[i]<<endl;
}
return 0;
}
C. 【例题3】数的划分
分析
设 f ( i , j ) f(i,j) f(i,j)为整数i分成 j 份时的方案数。
显然i<j时 f ( i , j ) = 0 f(i,j)=0 f(i,j)=0,i=j时 f ( i , j ) = 1 f(i,j)=1 f(i,j)=1.
其他状态:
有1 就拆1 那么
f
i
,
j
=
f
i
−
1
,
j
−
1
f_{i,j}=f_{i-1,j-1}
fi,j=fi−1,j−1
没有1 就在
f
i
−
j
,
j
f_{i-j,j}
fi−j,j 的基础上加1 11
递推式:
f
i
,
j
=
f
i
−
1
,
j
−
1
+
f
i
−
j
,
j
f_{i,j}=f_{i-1,j-1}+f_{i-j,j}
fi,j=fi−1,j−1+fi−j,j
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n,k,f[1001][1001];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
if(i==j) f[i][j]=1;
else if(i<j) f[i][j]=0;
else f[i][j]=f[i-1][j-1]+f[i-j][j];
}
}
cout<<f[n][k];
return 0;
}
D. 【例题4】传球游戏
分析
设 f ( i , j ) f(i,j) f(i,j)表示传球经过j个人后在第i个同学手上的方法数。
显然 i 的球会从i+1或i-1传过来,所以: f ( i , j ) = f ( i − 1 , j − 1 ) + f ( i + 1 , j − 1 ) f(i,j)=f(i-1,j-1)+f(i+1,j-1) f(i,j)=f(i−1,j−1)+f(i+1,j−1)
但是这题是一个环,处理一下i=1和i=n的情况,分别为:
i=1时:
f
(
i
,
j
)
=
f
(
n
,
j
−
1
)
+
f
(
i
+
1
,
j
−
1
)
f(i,j)=f(n,j-1)+f(i+1,j-1)
f(i,j)=f(n,j−1)+f(i+1,j−1)
i=n时:
f
(
i
,
j
)
=
f
(
1
,
j
−
1
)
+
f
(
i
−
1
,
j
−
1
)
f(i,j)=f(1,j-1)+f(i-1,j-1)
f(i,j)=f(1,j−1)+f(i−1,j−1)
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
//设f[i,j]表示传球经过j个人之后在第i个同学手上的方法数
int n,m,f[1001][1001];
int main()
{
cin>>n>>m;
f[1][0]=1;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(j==1) f[j][i]=f[j+1][i-1]+f[n][i-1];
else if(j==n) f[j][i]=f[1][i-1]+f[j-1][i-1];
else f[j][i]=f[j+1][i-1]+f[j-1][i-1];
}
}
cout<<f[1][m];
return 0;
}
E. 【例题5】平铺方案
分析
设f i 表示平铺2∗i矩形的方案数 分类:
在f i 放置一个竖的2*1矩形 方案数为 f i − 1 f_{i-1} fi−1
在f i 与 f i − 1 f_{i-1} fi−1 放置一个2∗2的矩形 方案数为 f i − 2 f_{i-2} fi−2
在f i与 f i − 1 f_{i-1} fi−1 放置两个横的2∗1矩形 方案数为 f i − 2 f_{i-2} fi−2
递推式
f
i
=
2
∗
f
i
−
2
+
f
i
−
1
f_i=2*f_{i-2}+f_{i-1}
fi=2∗fi−2+fi−1
注意n < = 250 做高精
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,f[1001][101];
void add()
{
f[0][1]=1;
f[1][1]=1;
f[2][1]=3;
for(int i=3;i<=250;i++)
{
for(int j=1;j<=80;j++)
{
f[i][j]+=f[i-1][j]+f[i-2][j]+f[i-2][j];
f[i][j+1]=f[i][j]/10;
f[i][j]=f[i][j]%10;
}
}
}
int main()
{
add();
while(scanf("%d",&n)!=EOF)
{
int ff=0;
for(int i=80;i>=1;i--)
{
if(f[n][i]!=0) ff=1;
if(ff==1)
{
cout<<f[n][i];
}
}
cout<<endl;
}
return 0;
}