【YbtOJ 1.1】 递推算法课堂过关

第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=(n1)(fn1+fn2)

上代码

#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[n1]

再把A 塔上1 个盘子移到C 塔 步数为1
最后把B 塔上n−1盘子移到C塔 步数为 f [ n − 1 ] f[n − 1] f[n1]

最终递推式: 2 ∗ f n − 1 + 1 2*f_{n-1}+1 2fn1+1

那么这道题就可以转化过来
f n表示n 盘子4 塔的最优步数 先将j个盘子从A 移到B 步数为f j

将剩下n−j个盘子移到D
剩余三塔 就可以用上面的递推式预处理出这个 f [ n − j ] f[n − j] f[nj]

最后将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=fi1,j1

没有1 就在 f i − j , j f_{i-j,j} fij,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=fi1,j1+fij,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(i1,j1)+f(i+1,j1)

但是这题是一个环,处理一下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,j1)+f(i+1,j1)
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,j1)+f(i1,j1)

上代码

#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} fi1

在f i 与 f i − 1 f_{i-1} fi1 放置一个2∗2的矩形 方案数为 f i − 2 f_{i-2} fi2

在f i与 f i − 1 f_{i-1} fi1 放置两个横的2∗1矩形 方案数为 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 做高精

上代码

#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;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值