已完成今日基础递推、递归、枚举大学习

递推

递推,指从某些已知条件出发,利用某种关系,依次推出所有结果。它的核心思想就是利用已知结果推导新的结果,来避免重复计算。最典型的例子就是斐波那契数列,它的已知条件就是 \(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;
}

题单

posted @ 2025-11-07 22:32  leizepromax  阅读(3)  评论(3)    收藏  举报