卡特兰数
一套有趣的题目
- 1,2,3…n 以此进栈,求有多少种可能的出栈序列。
- 由n对括号形成的合法的括号序列有多少个。
- N<=10^5
- 这个问题还有很多其他的表达形式。
- n个节点共能构成多少种二叉树,左右子树是认为不同。
- 凸多边形的三角划分的方案数:把一个凸多边形用n-3条直线连接n-3对顶点,共形成n-2个三角形,求方案数。(n+2边形三角划分)
- 一个n*n的格子,从(0,0)走到(n,n),求不跨过(0,0)->(n,n)这条直线的路径方案数
eg2:设f[i]表示i对括号的方案数,那么有:\(f[n]=\sum\limits_{i=0}^{n-1}f[i]*f[n-i-1]\)
卡特兰数:
我们设f[n]表示n个数依次进栈所能形成的出栈序列数。
似乎和之前不一样,好像不是划分成一段一段那样的简单形式。
我们可以考虑另一种形式的状态转移方式,以转移到子问题。
注意一段一段划分我们可以枚举最后一段的起点,但是这里不是一段一段的,我们要考虑另外的转移方式。
实际上我们发现我们可以枚举1这个数是什么时候出栈的。
那么我们可以得到:\(f[n]=\sum\limits_{i=0}^{n-1}f[i]*f[n-i-1]\)
一个经典题
有n个数,选择其中若干数,使得每连续k个数中都至少有一个数被选中,且选出的数的和最小。
k<=n<=1000
k<=n<=100000
SOLUTION:
首先我们定义f[i]表示按照满足题目要求的选择选取,并且第i个数一定被选中的最小和是多少;
f[1]=a[1];
\(f[i]=min\{f[j]+a[i]|i-j<=k\}\)
然后可以\(O(n^2)\)的做;
考虑优化:
\(f[i]=min\{f[j]|i-j<=k\}+a[i]\)
显然可以考虑单调队列优化w
用优先队列优化就好了√;
#include<bits/stdc++.h>
using namespace std;
int n,k;
int a[1010],dp[1010];
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
dp[1]=a[1];
for(int i=2;i<=n;i++) {
for(int j=i-1;i-j<=k&&j>=0;j--)
dp[i]=min(dp[i],dp[j]+a[i]);
//cout<<dp[i]<<endl;
}
printf("%d",dp[n]);
return 0;
}