ybtoj「基础算法」第1章 递推算法

A. 【例题1】错排问题

\(f_i\) 表示 \(i\) 个数时的答案 则 \(f_1=0\) \(f_2=1\)

则考虑把 \(i\) 放到第 \(k\) 个位置上在把 \(k\) 取出来 共 \(i-1\) 种方案

那么 \(i\) 已经被塞进去 考虑 \(k\) 放法:

  • 塞到除了 \(k\) 的其他 \(i-1\) 个位置上 和塞 \(i\) 时一样 答案 \(f_{i-1}\)

(此时前 \(i-1\) 除了 \(k\)\(k\) 这个位置 其他数&位置都存在 那么相当于把 \(k\) 当成最大数往里塞 那么 \(k\) 一定不会在 \(i\) 的位置上)

  • \(k\) 放到 \(i\) 的位置上 那么是剩下 \(i-2\) 位置错排 答案 \(f_{i-2}\)

递推式 \(f_i=(i-1)\times (f_{i-1}+f_{i-2})\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
#define ll long long
#define int ll
int n;
ll f[N];
signed main(){
    scanf("%lld",&n);
    f[1]=0,f[2]=1;
    for(int i=3;i<=n;i++)f[i]=(i-1)*(f[i-2]+f[i-1]);
    printf("%lld\n",f[n]);
    return 0;
}

B. 【例题2】传球游戏

显然 对于每个人最后答案都一样

钦定小蛮为1号

\(f_{i,j}\) 表示第 \(i\) 个人传 \(j\) 次传到他的方案数 答案即为 \(f_{1,m}\)

那么球一定是从左右的人传到他这里 \(f_{i,j}=f_{i-1,j-1}+f_{i+1,j-1}\) 记得特判1和n

起始状态 \(f_{1,0}=1\) (从小蛮手里传球)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline 
const int N=3e1+5;
const int mod=1e9+7;
int n,m,f[N][N];
int main(){
    scanf("%d%d",&n,&m);
    f[1][0]=1;
    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++){
            if(i==1){f[i][j]=f[n][j-1]+f[i+1][j-1];continue;}
            if(i==n){f[i][j]=f[i-1][j-1]+f[1][j-1];continue;}
            f[i][j]=f[i-1][j-1]+f[i+1][j-1];
        }
    }
    printf("%d",f[1][m]);
    return 0;
}

C. 【例题3】数的划分

\(f_{i,j}\) 表示把 \(i\) 分成 \(j\) 份的方案数

分成两种情况:

  • \(j\) 份其中一份是 \(1\):去掉 \(1\) 方案数 \(f_{i-1,j-1}\)
  • \(j\) 份中没有 \(1\) :每一份都减 \(1\) 方案数 \(f_{i-j,j}\)

\(f_{i,j}=f_{i-1,j-1}+f_{i-j,j}\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inl inline 
const int N=2e2+5;
const int mod=1e9+7;
int n;
ll f[N][N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)f[i][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            f[i][j]=f[i-1][j]+f[i][j-1];
    printf("%lld",f[n][n]);
    return 0;
}
posted @ 2023-10-23 10:01  xiang_xiang  阅读(5)  评论(0编辑  收藏  举报