卡特兰数

一套有趣的题目

  1. 1,2,3…n 以此进栈,求有多少种可能的出栈序列。
  2. 由n对括号形成的合法的括号序列有多少个。
    1. N<=10^5
    2. 这个问题还有很多其他的表达形式。
  3. n个节点共能构成多少种二叉树,左右子树是认为不同。
  4. 凸多边形的三角划分的方案数:把一个凸多边形用n-3条直线连接n-3对顶点,共形成n-2个三角形,求方案数。(n+2边形三角划分)
  5. 一个n*n的格子,从(0,0)走到(n,n),求不跨过(0,0)->(n,n)这条直线的路径方案数

eg2:设f[i]表示i对括号的方案数,那么有:(f[n]=sumlimits_{i=0}^{n-1}f[i]*f[n-i-1])

卡特兰数:

我们设f[n]表示n个数依次进栈所能形成的出栈序列数。

似乎和之前不一样,好像不是划分成一段一段那样的简单形式。

我们可以考虑另一种形式的状态转移方式,以转移到子问题。

注意一段一段划分我们可以枚举最后一段的起点,但是这里不是一段一段的,我们要考虑另外的转移方式。

实际上我们发现我们可以枚举1这个数是什么时候出栈的。

那么我们可以得到:(f[n]=sumlimits_{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;
}
原文地址:https://www.cnblogs.com/zhuier-xquan/p/11519665.html