已完成今日基础递推、递归、枚举大学习
递推
递推,指从某些已知条件出发,利用某种关系,依次推出所有结果。它的核心思想就是利用已知结果推导新的结果,来避免重复计算。最典型的例子就是斐波那契数列,它的已知条件就是 \(f_1=f_2=1\),递推式就是 \(f_i=f_{i-1}+f_{i-2}\)。
在实际解题时,一般题目会给出已知条件,某些稍难的题目需要自己分析已知条件。我们需要根据这些已知条件找到递推式,计算出答案。
数字三角形
题目链接:https://www.luogu.com.cn/problem/P1216
点击查看
发现从上往下走很麻烦,直接枚举会 T,正着走也很不好写,所以考虑从下往上递推。
那么此时的已知条件就是最后一层所有数的值,我们开始依次向上考虑。
假设我们现在在考虑倒数第二层。那么对于此时考虑的数 \(a_{i,j}\),它可以向下走到 \(a_{i+1,j}\) 或 \(a_{i+1,j+1}\)。题目要求答案最大,所以我们应该走到更大的那个数,那么此时 \(a_{i,j}\) 向下走的最大价值就是 \(a_{i,j}+\max (a_{i+1,j},a_{i+1,j+1})\),可以发现这是处在 \((i,j)\) 时的最优解,那么上面的数也可以像这样递推下去。
此时我们就知道了本题的递推式:\(a_{i,j}=a_{i,j}+\max (a_{i+1,j}+a_{i+1,j+1})\)。
#include<bits/stdc++.h>
using namespace std;
int n,a[1010][1010];
int main() {
cin>>n;for(int i=1;i<=n;++i) for(int j=1;j<=i;++j) cin>>a[i][j];
for(int i=n-1;i>=1;--i) for(int j=1;j<=i;++j) a[i][j]+=max(a[i+1][j],a[i+1][j+1]);
cout<<a[1][1];
return 0;
}
扫雷
题目链接:https://www.luogu.com.cn/problem/P2327
点击查看
容易发现,当第一个位置确定了以后,剩下的位置就都可以确定了,所以答案只能为 \(0、1、2\) 其中一个。
那么我们可以考虑枚举第一个位置是否有雷,然后向下递推,判断答案是否合法。设 \(f_i\) 表示考虑到第 \(i\) 个位置,当前位置有几颗雷。那么根据题目定义,我们就可以列出转移式:\(f_i=a_{i-1}-f_{i-1}-f_{i-2}\)。
然后我们枚举 \(f_1=0\) 和 \(f_1=1\),分别递推一下,判断是否合法即可。
#include<bits/stdc++.h>
using namespace std;
int f[10010],a[10010],n;
inline bool check(int x) {
f[1]=x;for(int i=2;i<=n;++i) f[i]=a[i-1]-f[i-1]-f[i-2];
for(int i=1;i<=n;++i)
if(f[i-1]+f[i]+f[i+1]!=a[i]||(f[i]!=1&&f[i]!=0)) return 0;
return 1;
}
signed main() {
cin>>n;for(int i=1;i<=n;++i) cin>>a[i];
cout<<check(1)+check(0);
return 0;
}
递归
递归,指将一个大的任务转化为若干个重复的子任务,通过不断调用自身来解决问题。对于一般的问题,递归通常要考虑两点:如何将问题转化为同类的子问题,转化边界以及如何解决最小的子问题。
赦免战俘
题目链接:https://www.luogu.com.cn/problem/P5461
点击查看
题面就差把递归两个字写脸上了......
递归函数传三个参表示左上角的坐标以及正方形长度,然后按题意模拟即可。
#include<bits/stdc++.h>
using namespace std;
int n,a[2010][2010];
inline void solve(int x,int y,int len) {
if(len==2) {a[x][y]=0;return ;}
for(int i=x;i<=x+len/2-1;++i)
for(int j=y;j<=y+len/2-1;++j) a[i][j]=0;
solve(x,y+len/2,len/2);solve(x+len/2,y,len/2);solve(x+len/2,y+len/2,len/2);
}
signed main() {
cin>>n;n=1<<n;
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=1;
solve(1,1,n);for(int i=1;i<=n;++i,cout<<'\n') for(int j=1;j<=n;++j) cout<<a[i][j]<<' ';
return 0;
}
递推和递归是很基础的思想,后面用到的很多算法都会用到这两种思想,比如动态规划、分治、以及许许多多的数据结构。
枚举
枚举,指列举所有可能的答案,分别计算是否满足条件。通常用在题目中有效的状态很少的时候。
密码锁
题目链接:https://www.luogu.com.cn/problem/P9752
点击查看
这题曾经在初三时击杀过我
发现所有的密码一共只有 \(10^5\) 个,考虑枚举。
由于转动的方式是只有固定两种的,所以我们考虑枚举转动幅度,然后对于 \(n\) 个密码锁的状态,记录能转到哪些密码。最后再枚举所有密码,判断是否能由 \(n\) 个密码转移过来即可。
#include<bits/stdc++.h>
using namespace std;
int f[10][10][10][10][10],n,a1,a2,a3,a4,a5,ans;
int main() {
cin>>n;
for(int i=1;i<=n;++i) {
cin>>a1>>a2>>a3>>a4>>a5;
for(int j=1;j<=9;++j) {
f[(a1+j)%10][a2][a3][a4][a5]++;
f[a1][(a2+j)%10][a3][a4][a5]++;
f[a1][a2][(a3+j)%10][a4][a5]++;
f[a1][a2][a3][(a4+j)%10][a5]++;
f[a1][a2][a3][a4][(a5+j)%10]++;
f[(a1+j)%10][(a2+j)%10][a3][a4][a5]++;
f[a1][(a2+j)%10][(a3+j)%10][a4][a5]++;
f[a1][a2][(a3+j)%10][(a4+j)%10][a5]++;
f[a1][a2][a3][(a4+j)%10][(a5+j)%10]++;
}
}
for(int i=0;i<=9;++i)
for(int j=0;j<=9;++j)
for(int k=0;k<=9;++k)
for(int l=0;l<=9;++l)
for(int r=0;r<=9;++r) ans+=(f[i][j][k][l][r]==n);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号