2月12日 模拟题 递推 题解

2月12日 模拟题 递推 题解
第一题十分经典的问题,我们看出这个问题其实可以从最小规模开始,作为一个整体,每次就只看最大一块圆盘以及上面一坨的移动,若我们用f[i]来表示规模为i时的移动数的话,那么我们只需把上面一坨先利用c移到b上,这要用f[i - 1]的移动次数,再把那最大的圆盘移到c上,最后再利用a把b上的一大坨转移到c上,这又要用f[i - 1]次移动次数。

         综上,我们得到递推方程f[i] = f[i -1] * 2 + 1

利用这个方程便可求出答案。

又我们把答案从 1 到 n 列出来,可以发现每一个答案都等于2的x(圆盘数)方 - 1,就可以很直接的写出以下代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
long long x = 1;
int main()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
{
x *= 2;
}
cout << x - 1;
return 0;

 } 

2月12日 模拟题 递推 题解

就这道题用到了一个问题划分的思想(本蒟蒻的理解方式),研究第一个或第一和第二个骨牌覆盖后剩余的覆盖方案:

我们依然用f[i]来表示当行列数为i时,摆放的方式数。

当i = 1 的时候摆放方式只有一种     i = 0 时纯粹是我们的假象边界,赋值为1.

    1、第一个骨牌竖着放:那么这种情况下剩下的格数就是2*(i-1)格,方式数就是f[i - 1]种。

    2、第一第二个骨牌都横着放,把第一个4*4的空间给占了:那么现在剩下2*(i - 2)格的空间,方式数就是f[i - 2]种。

那么总问题就应该是f[i] = f[i - 1] + f[i - 2] 啦!

实际上就是斐波拉契数列

这样我们可以写出:

#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
long long a[55]; 
int main()
{
int n;
cin >> n;
a[0] = a[1] = 1;
for(int i = 2;i <= n;i++)
{
a[i] = a[i-1]+a[i-2];
}
cout << a[n];
return 0;

}

代码十分简单,但是理解的话本蒟蒻还是觉得站在蒟蒻的角度是不太好理解的。

2月12日 模拟题 递推 题解

一道状态转移的题

    总而言之意思就是每个人可以传给旁边的俩人,可以传的次数为m,人数为n,就相当于一个圈,向左或向右传。

那么我们可以定状态为:f[i][j] 意为经j次传递传到i编号处的人时的总方案数。

因为成圈型结构,我们肯定要求模,所以从0开始计数。

边界有两个:

f[0][0] 当传递次数为 0 时传到 0号(也就是小蛮时)方案数为1

f[i][0] (i != 0)  //当传递不了而球又不在小蛮手上时,方案数为0. 

某位置的方案数应该等于从左传到右加上从右传到左的方案总和,所以如下:
    f[i][j] = f[(i - 1 + n) % n][j - 1] + f[(i + 1) % n][j - 1]; 

    代码:

#include<bits/stdc++.h>
using namespace std;
long long f[35][35];
int main()
{
int n,m;
cin >> n >> m;
f[0][0] = 1;//边界,当传递次数为 0 时传到 0号(也就是小蛮时)方案数为1 
for(int i = 1;i < n;i++)
{
f[i][0] = 0;//当传递不了而球又不在小蛮手上时,方案数为0. 
}
for(int j = 1;j <= m;j++)
{
for(int i = 0;i < n;i++)
{
f[i][j] = f[(i - 1 + n) % n][j - 1] + f[(i + 1) % n][j - 1]; 
}//f[i][j]表示当传递次数为j时传到i号时的方案数。
}
cout << f[0][m]; 
return 0;

2月12日 模拟题 递推 题解

这道题首先我们看到给出n个球,r个盒子,并且每个盒子都得有东西。

那么要解决这道题我们可以定义状态:

    f[i][j] 表示当放球编号(也就是数量啦这道题中的话)为i放入了j个盒子·时所有的方案数

这样边界便可直接得出:

    1、当盒子只有一个时(j == 1),我们只有一种放的方式,就是全部放进去。当球数和盒子数一样时(i == j),由于不能空盒,则只能一个盒子放一个,所以方案数也为1。

    2、当球数小于盒子数时(i < j),由于不能有空盒,所以这样的方案数为0。

状态转移则需对任一编号球放入某盒子做分析:

1、当第i个球独自占有第j个盒子时,其他球放的方案就为f[i - 1][j - 1]

2、当第i个球和其他某些球一起在某盒子里时,由于放入盒子是随机的,则就有j种可能,所以此时方案数为f[i - 1][j]*j。

所以说代码如下:

#include<bits/stdc++.h>
using namespace std;
long long f[25][25];


int main()
{
int n,m;
cin >> n >> m;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
if(j == 1 || 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 - 1][j]*j;

}

                }

}
cout << f[n][m];
return 0;
}

2月12日 模拟题 递推 题解

错排:

是一道简单的代码,坑爹的推导过程的一道题。

如果你够厉害的话,可以直接把规律方程写出来,那么你这道题就AC了。

0 1 2 9 44 。。。。。(加油)

但本蒟蒻是没有做到的,于是乎我们就开始分析吧。

定义状态为:f[i]当人数为i时的方案数。

这道题边界是:

    1、一个人时,不能交换,方案数为0。

    2、俩人时,只能互相交换,方案数为1.

对某个同学x给一号同学书分析:

1、他刚好给了1号同学,一号同学的书也可以说给了他(毕竟x是随机某一个,我们可以就定为1号同学给的那个嘛),那么此时剩下i-2个人要互相交换书,方案数为f[i - 2]。

2、他没能给到1号同学,但我们可以假设一号同学把书给了他,理由同上,那么此时还有i - 1个人要互相交换书,所以方案数为f[i - 1]。

但要知道我们的x同学是任一同学,那么x的可能就有(i - 1)种(因为给了一号同学书,一号不可能为x),所以总方案数要乘以一个(i - 1)。

代码如下:

#include<bits/stdc++.h>
using namespace std;
long long f[25];
int main()
{
int n;
cin >> n;
f[1] = 0;
f[2] = 1;
for(int i = 3;i <= n;i++)//状态是i本书时的排列方式总数,
//看的是一本书交换后的两种情况之和
{
f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
}
cout << f[n];
return 0;
 } 

就特简单,就让人觉得推理过程可怕。

接下来转到卡特兰数环节!