YbtOJ 「基础算法」第1章 递推算法
例题 1 错排问题
\(f_i\) 表示前 \(i\) 个数的错排。易得递推式为 \(f_i=(i-1)\times(f_{i-1}+f_{i-2})\)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,f[25];
signed main()
{
scanf("%lld",&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]<<endl;
return 0;
}
例题 2 传球游戏
设 \(f_{i,j}\) 表示经过 \(j\) 次传到第 \(i\) 个人的方案数。则 \(f_{i,j}=f_{i-1,j-1}+f_{i+1,j-1}\)。注意 \(j\) 要在外层循环。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,f[35][35];
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++)
{
int l=i-1,r=i+1;
if(l<1) l=n;
if(r>n) r=1;
f[i][j]=f[l][j-1]+f[r][j-1];
}
}
cout<<f[1][m]<<endl;
return 0;
}
例题 3 数的划分
设 \(f_{i,j}\) 表示把 \(i\) 分成 \(j\) 份方案数。
分情况讨论:
- 若方案中不含 \(1\),则可以由每个数都减一的情况转移得到,方案数为 \(f_{i-j,j}\)。
- 若含有 \(1\),则方案数为 \(f_{i-1,j-1}\)。
故总转移方程为 \(f_{i,j}=f_{i-j,j}+f_{i-1,j-1}\)。
code
#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[205][10];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i<=j) f[i][j]=(i==j);
else f[i][j]=f[i-1][j-1]+f[i-j][j];
}
}
cout<<f[n][m]<<endl;
return 0;
}
//7 3
例题 4 栈的问题
设入栈为 \(+1\),出栈为 \(-1\),要求中间过程中和不得小于 \(0\)。则题目转化为卡特兰数问题,直接套公式即可。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,f[50];
signed main()
{
scanf("%lld",&n);
f[0]=1;
for(int i=1;i<=n;i++) f[i]=f[i-1]*(4*i-2)/(i+1);
cout<<f[n]<<endl;
return 0;
}
1.划分数列
设 \(f_i,g_i\) 分别表示划分到第 \(i\) 位时,当前段为上升/下降时的最小段数。
转移方程见代码。(懒
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,a[N],f[N],g[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
f[1]=g[1]=1;
for(int i=2;i<=n;i++)
{
if(a[i]>a[i-1]) f[i]=min(f[i-1],g[i-1]+1),g[i]=min(g[i-1],f[i-1])+1;
else if(a[i]==a[i-1]) f[i]=min(f[i-1],g[i-1]+1),g[i]=min(g[i-1],f[i-1]+1);
else f[i]=min(f[i-1],g[i-1])+1,g[i]=min(g[i-1],f[i-1]+1);
}
cout<<min(f[n],g[n])<<endl;
return 0;
}
2.求 f 函数
直接根据题意模拟时间开销过大,仿照记忆化搜索的思路,开数组存下已经求过的内容即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,f[N],ans;
int find(int x)
{
if(f[x]) return f[x];
if(x>=1001) return f[x]=x-10;
else
{
return f[x]=find(find(x+11));
}
}
int main()
{
while(1)
{
scanf("%d",&n);
if(n==0) break;
//cout<<find(n)<<endl;
ans^=find(n);
}
cout<<ans<<endl;
return 0;
}
3.无限序列
手算该数列的前几次变化情况,可得每次变换得到的数列为前两次的数列拼接而成。
分别递推维护序列长度及对应 \(1\) 的数量。
将 \([a,b]\) 中 \(1\) 的数量转化为前缀和相减的形式,不断二分减小 \(a,b\) 的值求出答案。
注意防止爆 long long。
code
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int N=1e6+5;
int Q,a,b,n;
struct node{
int len,cnt;
}f[N];
int sum(int x)
{
int ans=0;
while(x)
{
int l=1,r=n;
if(f[n].len<=x) {x-=f[n].len;continue;}
while(l<r)
{
int mid=(l+r+1)>>1;
if(f[mid].len<=x) l=mid;
else r=mid-1;
}
x-=f[l].len;ans+=f[l].cnt;
}
return ans;
}
signed main()
{
scanf("%llu",&Q);
f[1].len=f[1].cnt=f[2].cnt=1;
f[2].len=2;
for(int i=3;;i++)
{
f[i].len=f[i-1].len+f[i-2].len;
f[i].cnt=f[i-1].cnt+f[i-2].cnt;
if((int)pow(2,63)-f[i].cnt<=f[i-1].cnt) {n=i;break;}
}
while(Q--)
{
scanf("%llu%llu",&a,&b);
//maxn=max(a,b);
cout<<sum(b)-sum(a-1)<<endl;
}
return 0;
}
4.序列个数
首先将题意转化为在 \(n\times n\) 的方阵里填 \(1\),且每行每列都保证只有一个 \(1\),则每一种填数方式对应着一种排列。(不过讲真这个神仙转换方法我没想到/wq
从左上角开始一圈圈填数,转移过程分类讨论。
当 \(a_i-a_{i-1}<0\) 或 \(a_i-a_{i-1}>2\) 时无解。
\(0\),这一圈不用填,即 \(f_{i}=f_{i-1}\)。
\(1\),这一圈有 \(2i-1\) 个位置,其中横行或竖行被以前填的数占用的有 \(2\times a_{i-1}\) 个。故 \(f_i=f_{i-1}\times (2i-1-2\times a_{i-1})\)。
\(2\),只能在横行填一个竖行填一个,\(f_i=f_{i-1}\times (i-1-a_{i-1})^2\)。
注意取模。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
const int mod=340610;
int n,a[N],f[N];
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
f[1]=1;
for(int i=2;i<=n;i++)
{
if(a[i]<a[i-1]) {cout<<"0"<<endl;return 0;}
else if(a[i]==a[i-1]) f[i]=f[i-1];
else if(a[i]-a[i-1]==1) f[i]=f[i-1]*(2*i-1-2*a[i-1])%mod;
else if(a[i]-a[i-1]==2) f[i]=f[i-1]*(i-1-a[i-1])%mod*(i-1-a[i-1])%mod;
else {cout<<"0"<<endl;return 0;}
}
cout<<f[n]%mod<<endl;
return 0;
}
5.等距跳跃
其实我也不知道 \(O(n^3)\) 的复杂度怎么跑过的,大概是玄学吧(
枚举两个点暴力判断即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
int r,c,n,maxn;
int tot;
struct node{
int x,y;
}a[N];
bool cmp(node e,node f){
if(e.x==f.x) return e.y<f.y;
else return e.x<f.x;
}
int mp[N][N];
int main()
{
scanf("%d%d%d",&r,&c,&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
mp[a[i].x][a[i].y]++;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
int dux=2*a[i].x-a[j].x,duy=2*a[i].y-a[j].y;
if(dux>=1&&dux<=r&&duy>=1&&duy<=c) continue;
int xx=a[i].x,yy=a[i].y;
int dx=a[j].x-a[i].x,dy=a[j].y-a[i].y;
int ans=1;
while(xx<=r&&yy<=c)
{
xx+=dx,yy+=dy;
if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&(!mp[xx][yy])) {ans=0;break;}
if(xx>=1&&xx<=r&&yy>=1&&yy<=c) ans++;
else break;
}
//if(ans>=maxn) {cout<<ans<<" "<<a[i].x<<" "<<a[i].y<<" "<<a[j].x<<" "<<a[j].y<<endl;}
if(ans>=3) maxn=max(ans,maxn);
}
}
cout<<maxn<<endl;
return 0;
}
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/16581484.html