【Ybt OJ】[基础算法 第1章]递推算法

「 「 基础算法 」 」 第1章 递推算法
目录:

A.错排问题
B.奇怪汉诺塔
C.数的划分
D.传球游戏
E.平铺方案

A . A. A. 例题 1 1 1 错排问题

洛谷 l i n k link link
在这里插入图片描述

分析:

考虑第 n n n个元素 放在 k k k位上 那么有 ( n − 1 ) (n-1) (n1)种方案 ( k ≠ n ) (k≠n) (k=n)
再考虑这个 k k k 当它在 n n n位时 就有由于 n , k n,k n,k两元素位置相同 其他 n − 2 n-2 n2个元素错排即可
k k k不在 n n n位时 直接 n − 1 n-1 n1个元素错排
递推式 f n = ( n − 1 ) ∗ ( f n − 1 + f n − 2 ) f_n=(n-1)*(f_{n-1}+f_{n-2}) fn=(n1)(fn1+fn2)

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 n1个盘子移到 B B B塔 则步数为 f n − 1 f_{n-1} fn1 再把 A A A塔上 1 1 1个盘子移到 C C C塔 步数为 1 1 1
最后把 B B B塔上 n − 1 n-1 n1盘子移到 C C C塔 步数为 f n − 1 f_{n-1} fn1
最终递推式 2 ∗ f n − 1 + 1 2*f_{n-1}+1 2fn1+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 nj个盘子移到 D D D
剩余三塔 就可以用上面的递推式预处理出这个 f n − j f_{n-j} fnj
最后将 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 数的划分

洛谷 l i n k link link
在这里插入图片描述

分析:

可以 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=fi1,j1 没有 1 1 1就在 f i − j , j f_{i-j,j} fij,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=fi1,j1+fij,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 传球游戏

洛谷 l i n k link link
在这里插入图片描述

分析:

我们设 f i , j f_{i,j} fi,j表示经过 i i i轮传递到第 j j j个同学的方案数
由于球可以从第 j − 1 j-1 j1 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=fi1,j1+fi1,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 2i矩形的方案数 分类:
f i f_i fi放置一个竖的 2 ∗ 1 2*1 21矩形 方案数 f i − 1 f_{i-1} fi1
f i f_i fi f i − 1 f_{i-1} fi1放置一个 2 ∗ 2 2*2 22的矩形 方案数 f i − 2 f_{i-2} fi2
f i f_i fi f i − 1 f_{i-1} fi1放置两个横的 2 ∗ 1 2*1 21矩形 方案数 f i − 2 f_{i-2} fi2
递推式 f i = 2 ∗ f i − 2 + f i − 1 f_i=2*f_{i-2}+f_{i-1} fi=2fi2+fi1
注意 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;
}

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/111640777