YbtOJ 「基础算法」第1章 递推算法
例题 1 错排问题
表示前 个数的错排。易得递推式为 。
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 传球游戏
设 表示经过 次传到第 个人的方案数。则 。注意 要在外层循环。
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 数的划分
设 表示把 分成 份方案数。
分情况讨论:
- 若方案中不含 ,则可以由每个数都减一的情况转移得到,方案数为 。
- 若含有 ,则方案数为 。
故总转移方程为 。
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 栈的问题
设入栈为 ,出栈为 ,要求中间过程中和不得小于 。则题目转化为卡特兰数问题,直接套公式即可。
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.划分数列
设 分别表示划分到第 位时,当前段为上升/下降时的最小段数。
转移方程见代码。(懒
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.无限序列
手算该数列的前几次变化情况,可得每次变换得到的数列为前两次的数列拼接而成。
分别递推维护序列长度及对应 的数量。
将 中 的数量转化为前缀和相减的形式,不断二分减小 的值求出答案。
注意防止爆 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.序列个数
首先将题意转化为在 的方阵里填 ,且每行每列都保证只有一个 ,则每一种填数方式对应着一种排列。(不过讲真这个神仙转换方法我没想到/wq
从左上角开始一圈圈填数,转移过程分类讨论。
当 或 时无解。
,这一圈不用填,即 。
,这一圈有 个位置,其中横行或竖行被以前填的数占用的有 个。故 。
,只能在横行填一个竖行填一个,。
注意取模。
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.等距跳跃
其实我也不知道 的复杂度怎么跑过的,大概是玄学吧(
枚举两个点暴力判断即可。
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-08-12 8.12 dp模拟赛总结