进出栈序列问题
1–N N个整数和一个无限大的栈 每个数要进出栈一次,进栈顺序为1,2,…,N,可能出栈的顺序有多少种
这个题求的就是一个catalan数
(
2
n
n
)
1
n
+
1
\binom{2n}{n}\frac{1}{n+1}
(n2n)n+11
有两种dp方法 一种一维dp 数组的S,S[N]表示进栈顺序为1,2,3,…,N时可能的出栈序列总数。
S[N]=
∑
K
=
1
N
S
k
−
1
∗
S
N
−
K
\sum_{K=1}^NS_{k-1}*S_{N-K}
∑K=1NSk−1∗SN−K
int n;
while(cin>>n){
vector<int> S(n+1);
S[0] = 1;
for(int i=1;i<=n;i++)
for(int k=1;k<=i;k++)
S[i] += S[k-1]*S[i-k];
cout<<S[n]<<endl;
}
时间复杂度为 O ( n 2 ) O(n^2) O(n2)
另一种dp是用二维数组 dp[i][j] 表示有i个数尚未进栈,目前有j个数在栈里,已经有n-i-j个数出栈的方法总数 dp[0][i] = 1 (i=0,1,2,3…,n) 因为只能不停的出栈 所以方法数只能为1 要求的目标是dp[n][0]
处于(i,j)状态的dp 要么进栈 要么出栈 即会分离成两种dp状态
dp[i][j] = dp[i-1][j+1] + dp[i][j-1] 前面是进栈 后面是出栈
数据限制: 首先元素总数为n 还没进栈和已经出栈的总数不可能超过n 即
i
+
j
<
=
n
i+j<=n
i+j<=n
当
j
=
0
j=0
j=0时 栈为空 不能进行出栈操作 所以j为0是 dp[i][j] = dp[i-1][j+1]
int n=0;
while(cin>>n){
vector<vector<int> > dp(n+1,vector<int>(n+1));
for(int i=0;i<n+1;i++)
dp[0][i] = 1;
for(int i=1;i<=n;i++)
for(int j=0;j<=n-i;j++)
dp[i][j] = dp[i-1][j+1] + (j==0 ? 0 : dp[i][j-1]);
cout<<dp[n][0]<<endl;
}
POJ 2559
这题不会做,看了一下书上的提示,先考虑矩形的高度是单调增加的,然后考虑一般情况的情况。
用的是单调栈 栈里面的元素的值严格单调递增,碰见比栈顶小的数 依次把栈的元素弹出,直到栈顶的数小于等于num,用width记录栈中每个元素的宽度,如果遇见与栈顶相等的元素 那么就把栈顶元素对应的宽度加1。弹出的时候 由于弹出的矩形高度都大于num 所以最后这些被弹出的矩形的宽度要加到num对应的宽度上去
我的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<sstream>
using namespace std;
typedef long long ll;
ll st[100007];
int width[100007];
int main()
{
while(true){
int n,top = 0,num,w = 0;
ll mx = 0;
cin>>n;
if(n == 0)
break;
n++;
while(n--){
if(n >= 1)
scanf("%d",&num);
else
num = 0;//在输入序列中额外增加一个为0的输入
if(num > st[top]){
st[++top] = num;
width[++w] = 1;
}
else{
ll t = 0,wt = 0;
while(top > 0 && num < st[top]){
wt += width[w--];
t = st[top--] * wt;
if(t > mx)
mx = t;
}
if(num == st[top])
width[w] += wt+1;
}
}
cout<<mx<<endl;
}
return 0;
}
wrong answer了 发现栈弹出的时候 还有一种st[top]
<
<
< num的情况 那么应该在栈里加入num
修改为:
if(num == st[top])
width[w] += wt+1;
else{
st[++top] = num;
width[++w] = wt+1;
}
改完以后AC了 500K 141ms