P1044 栈题解
一、深搜
别的也不会,一个深搜走天下!深搜我们主要关心的是下一步噢~
怎么个深搜法呢?我们模拟一下,有一个装个顺序号小球的队列,一个个准备放到一个栈里。一共几下面几种场景:
1、队列为空,栈为空。
这种场景的下一步
就只能是“游戏终止”,而“游戏终止”时我们应该方案数+1。
2、队列为空,栈不空。
这种场景下下一步
只能是栈弹出一个,一直到弹空了为止,也就是,方案数+1。
3、队列不空,栈为空。
这种场景下下一步
只能是队列出来一个,栈进去一个,场景变为“队列-1,栈+1”。
4、队列不空,栈不空。
这种场景下下一步
有点麻烦,可以是队列-1,栈+1;也可以是队列不动,栈-1。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
// i 表示队列里还有几个待排的数,
// j 表示栈里有 j 个数,dfs(i,j)表示此时的情况数
// 不重不漏的描述了所有情况
LL dfs(int i, int j) {
LL ans = 0;
//(1)队列空,栈空
if (i == 0 && j == 0)ans += 1; //这是递归出口,增加一种方法
//(2)队列空,栈不空
if (i == 0 && j > 0) ans += 1; //只能一个个蹦出去啦~
//(3)队列不空,栈空
if (i > 0 && j == 0) ans += dfs(i - 1, j + 1); //从队列中取一个,放入到栈中
//(4)队列不空,栈不空
if (i > 0 && j > 0) ans += dfs(i, j - 1) + dfs(i - 1, j + 1);
//有两种选择,一是队列不动,栈中出去一个;另一种是队列取一个,放入栈中
return ans;
}
int main() {
cin >> n;
printf("%lld", dfs(n, 0));
return 0;
}
结果5个测试点,过了4个,最后一个TLE了~
2、记忆化搜索
我们观察到,上面的代码之所以超时,根源是因为这个和前面“过河卒”一样,存在大量重复计算,到达某个点后,这个点再出发都是重复的。可以考虑使用反向思维用递推!当然,本题也可以使用记化搜索来解决:
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
typedef long long LL;
int n;
// 定义一个二维数组f[i,j]f[i,j],用下标 i 表示队列里还有几个待排的数,
// j 表示栈里有 j 个数,f[i,j]表示此时的情况数
// 不重不漏的描述了所有情况
LL f[N][N]; //记忆化搜索
LL dfs(int i, int j) {
//算过
if (f[i][j]) return f[i][j];
//(1)队列空,栈空
if (i == 0 && j == 0) f[i][j] += 1; //这是递归出口,增加一种方法
//(2)队列空,栈不空
if (i == 0 && j > 0) f[i][j] += 1;//只能一个个蹦出去啦~
//(3)队列不空,栈空
if (i > 0 && j == 0) f[i][j] += dfs(i - 1, j + 1);//从队列中取一个,放入到栈中
//(4)队列不空,栈不空
if (i > 0 && j > 0) f[i][j] += dfs(i, j - 1) + dfs(i - 1, j + 1);
//有两种选择,一是队列不动,栈中出去一个;另一种是队列取一个,放入栈中
return f[i][j];
}
int main() {
cin >> n;
printf("%lld", dfs(n, 0));
return 0;
}
使用记化搜索后,成功AC本题。
3、递推
既然可以使用递推,那么递推的步骤和思路是什么样的呢?
1、边界值
一定要有小的方向的边界!!!而且特别要注意元素个数是0的情况!!!
2、求啥就定义啥,按需求定义数组
代表了个元素的所有出管方法数量。
3、对个元素的大场景进行子集合的切分,要不重不漏,一般是按最后一个不相同的点切分。
本题是按最后一个出栈元素进行划分开,分情况讨论。
对于从来讲,第个小球是最后一个出栈的,那么在它之前有个元素先入栈,先出栈;在它之后,有个小球后入栈,后出栈。那么,以它为最后一个出栈的方案数量就是(乘法原理)
对于个小球来讲,就是如下的伪代码示意:
for(int j=1;j<=i;j++)
dp[i]+=dp[j-1]*dp[i-j];//所有子集合的方案数量和,加法原理
完整代码
#include <bits/stdc++.h>
using namespace std;
// h[i]:i个元素一共有h[i]种出管方式
// 0个元素,只有一种情况,这种情况就是啥也不出,啥也不出也算是一种场景。
// 1个元素,只有一种情况,就是出队列,进栈,出栈。
int n, dp[20] = {1, 1};
int main() {
cin >> n;
for (int i = 2; i <= n; i++)
for (int j = 1; j <= i; j++)//最后出栈的元素假设是j
dp[i] += dp[j-1] * dp[i - j];//所有的可能性加在一起
printf("%d", dp[n]);
return 0;
}
4、卡特兰数解法
为什么会想到用卡特兰数公式来解这道题呢?
超棒的讲解
https://www.bilibili.com/video/BV1nE411A7ST?from=search&seid=2618099886973795159
#include <bits/stdc++.h>
// 超棒的讲解
// https://www.bilibili.com/video/BV1nE411A7ST?from=search&seid=2618099886973795159
using namespace std;
/**
如果n=1 1
如果n=2 2
如果n=3 5
如果n=4 14
如果n=5 42
如果n=6 132
如果n=7 429
如果n=8 1430
如果n=9 4862
如果n=10 16796
如果n=11 58786
如果n=12 208012
如果n=13 742900
如果n=14 2674440
如果n=15 9694845
如果n=16 35357670
如果n=17 129644790
如果n=18 477638700
卡特兰数!!!
*/
int catalan(int n) {
if (n == 0 || n == 1) return 1;
int res = 0;
for (int i = 1; i <= n; i++) res += catalan(i - 1) * catalan(n - i);
return res;
}
int main() {
int n;
cin >> n;
cout << catalan(n) << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2016-07-16 中控考勤机WEB主动上报接收SERVER程序