「 「 「基础算法 」 」 」第1章 递推算法
目录:
A.错排问题
B.奇怪汉诺塔
C.数的划分
D.传球游戏
E.平铺方案
A . A. A. 例题 1 1 1 错排问题
分析:
考虑第 n n n个元素 放在 k k k位上 那么有 ( n − 1 ) (n-1) (n−1)种方案 ( k ≠ n ) (k≠n) (k=n)
再考虑这个 k k k 当它在 n n n位时 就有由于 n , k n,k n,k两元素位置相同 其他 n − 2 n-2 n−2个元素错排即可
当 k k k不在 n n n位时 直接 n − 1 n-1 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)
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
long long f[25];
int main(){
scanf("%d",&n);
f[1]=0;f[2]=1;
for(int i=3;i<=n;i++)
f[i]=(i-1)*(f[i-1]+f[i-2]); //递推
printf("%lld",f[n]);
return 0;
}
B . B. B. 例题 2 2 2 奇怪汉诺塔
分析:
看到这个 n n n这么小 直接打表
考虑只有 3 3 3座塔的汉诺塔问题 设 f n f_n fn为 n n n盘子 3 3 3塔的最优步数
先把 n − 1 n-1 n−1个盘子移到 B B B塔 则步数为 f n − 1 f_{n-1} fn−1 再把 A A A塔上 1 1 1个盘子移到 C C C塔 步数为 1 1 1
最后把 B B B塔上 n − 1 n-1 n−1盘子移到 C C C塔 步数为 f n − 1 f_{n-1} fn−1
最终递推式: 2 ∗ f n − 1 + 1 2*f_{n-1}+1 2∗fn−1+1 那么这道题就可以转化过来
f n f_n fn表示 n n n盘子 4 4 4塔的最优步数 先将 j j j个盘子从 A A A移到 B B B 步数为 f j f_j fj 将剩下 n − j n-j n−j个盘子移到 D D D
剩余三塔 就可以用上面的递推式预处理出这个 f n − j f_{n-j} fn−j
最后将 B B B的 j j j个盘子移到 D D D 步数为 f j f_j fj
再把它们加起来取 m i n min min即可.
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int d[13],f[13];
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]=0x3f3f3f3f;
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++) printf("%d\n",f[i]);
return 0;
}
C . C. C. 例题 3 3 3 数的划分
分析:
可以 d f s dfs dfs 但时效不够有 200 200 200~ 300 m s 300ms 300ms
设 f i , j f_{i,j} fi,j表示整数 i i i分为 j j j份的方案数
那么显然 i < j i<j i<j时 f i , j = 0 f_{i,j}=0 fi,j=0 ; i = j i=j i=j时 f i , j = 1 f_{i,j}=1 fi,j=1
其他状态:
有 1 1 1就拆 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 1 1就在 f i − j , j f_{i-j,j} fi−j,j的基础上加 1 1 1
递推式: 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
CODE:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define reg register
using namespace std;
int n,k,f[205][12];
int main()
{
scanf("%d%d",&n,&k);
for(reg int i=1;i<=n;i++)
for(reg 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];
}
printf("%d",f[n][k]);
return 0;
}
D . D. D. 例题 4 4 4 传球游戏
分析:
我们设 f i , j f_{i,j} fi,j表示经过 i i i轮传递到第 j j j个同学的方案数
由于球可以从第 j − 1 j-1 j−1和 j + 1 j+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} fi,j=fi−1,j−1+fi−1,j+1
但由于这些人坐成了一个环 我们需特别处理一下第 n n n位和第 1 1 1位即可.
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,f[50][50];
int main(){
scanf("%d%d",&n,&m);
f[0][1]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(j==1) f[i][j]=f[i-1][n]+f[i-1][j+1];
else if(j==n) f[i][j]=f[i-1][n-1]+f[i-1][1]; //特别处理
else f[i][j]=f[i-1][j-1]+f[i-1][j+1];
}
printf("%d",f[m][1]);
return 0;
}
E . E. E. 例题 5 5 5 平铺方案
分析:
设 f i f_i fi表示平铺 2 ∗ i 2*i 2∗i矩形的方案数 分类:
在 f i f_i fi放置一个竖的 2 ∗ 1 2*1 2∗1矩形 方案数为 f i − 1 f_{i-1} fi−1
在 f i f_i fi与 f i − 1 f_{i-1} fi−1放置一个 2 ∗ 2 2*2 2∗2的矩形 方案数为 f i − 2 f_{i-2} fi−2
在 f i f_i fi与 f i − 1 f_{i-1} fi−1放置两个横的 2 ∗ 1 2*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 n<=250 n<=250 做高精
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n;
struct node{
int val[103];
}a[255];
void add(int x)
{
for(int i=1;i<=100;i++) //100位数字 随便开
{
a[x].val[i]+=a[x-2].val[i]*2+a[x-1].val[i]; //递推式
a[x].val[i+1]=a[x].val[i]/10; //高精
a[x].val[i]%=10;
}
}
void print(int x){
int p=100;
while(a[x].val[p]==0) p--;
while(p)
{
printf("%d",a[x].val[p]);p--;} //输出
printf("\n");
}
int main(){
a[0].val[1]=a[1].val[1]=1;
a[2].val[1]=3;
for(int i=3;i<=250;i++) add(i);
while(scanf("%d",&n)!=EOF)
print(n);
return 0;
}