洛谷P1044 棧 淺談卡特蘭數


洛谷P1044


標籤

  • 卡特蘭數

前言


簡明題意

  • 最經典的卡特蘭數引入場景。給一個序列,每次可以從序列的頭部入棧,或者從棧中彈出元素插入到序列的尾部,問有多少種出棧順序

思路

  • 首先複習一下卡特蘭數。卡特蘭數19不爆int,35不爆longlong。其次是卡特蘭數的計算方法,s(0)=1s(0)=1第一個遞推式s(n)=i=0n1s(i)s(n1i)s(n)=\sum\limits_{i=0}^{n-1}s(i)*s(n-1-i),複雜度n2n^2。第二個是通項s(n)=2n!n!(n+1)!s(n)=\frac{2n!}{n!(n+1)!},階乘20不爆longlong,12不爆int,因此用通項法最多隻能求到s(10).
  • 然後講一講卡特蘭數到底怎麼推的。假設有n個數進行進出棧操作,那麼我們知道這n個數有2n個進出棧次序(因爲每個數都要進一次棧又要出一次棧)。現在設入棧爲+1,出棧爲-1,顯然我們要從我們要從2n中選出n個+1,其餘的地方填-1就行了,所以就有C2nnC_{2n}^n種填法。那麼答案就是C2nnC_{2n}^n嗎?其實不是的,看這樣一個序列:-1 -1 -1 1 1 1,顯然一開始棧裏都沒有元素怎麼出棧呢?所以我們需要保證選出來的序列,對於任意k<=n,有i=1k>=0\sum_{i=1}^k>=0,這樣的序列纔是符合要求的。所以現在求一下不符合要求的序列,也就是求i=1k<0\sum_{i=1}^k<0的數量。我們這樣考慮,對於一個不符合要求的序列例如+1 -1 -1 +1,我們從第一個元素開始直到第一次不符合要求的那個元素結束,將這些元素取相反數,這個例子中取了相反數就是-1 +1 +1 +1,現在令不符合要求的序列集合爲M,取反後的序列集合爲N,我們可以知道N到M是一一映射的,也就是說,集合M的大小(不符合要求的數量)等於集合N的大小,而集合N,是由n+1個+1和n-1個-1組成的,那麼,集合N的大小等於C2nn1C_{2n}^{n-1},也就是不符合要求的序列數量是C2nn1C_{2n}^{n-1},而總的數量是C2nnC_{2n}^n,所以符合要求的數量是:C2nnC2nn1C_{2n}^n-C_{2n}^{n-1}
  • 然後我們可以更深一步,探討一下卡特蘭數的變形。回憶之前的情形,+1和-1的數量是相等的,下一種情形,+1和-1的數量不相等。我們簡化一下題目,就不引入情景了。現在有n個+1,m個-1,要使得前綴和永遠>=0,問有多少種排列方法。這就需要引入集合方法,我們建立一個n*m的表格
    在這裏插入圖片描述
    +1往右,-1往上,那麼爲了滿足要求,-1的數量在任意時刻不能多餘+1的數量,也就是往上的線不能超過中線。我們先舉一個犯規的例子:
    在這裏插入圖片描述我們仍然建立一個像普通卡特蘭數的映射,將第一次超過中線的數以及之前的所有取反,然後將剩餘的平移過來:
    在這裏插入圖片描述
    我們發現,又和卡特蘭數的普通情形一樣了,+1多了一次選擇,-1少了一次選擇,於是犯規的數目就是Cn+mn1C_{n+m}^{n-1},所以符合要求的數目是:Cn+mnCn+mn1C_{n+m}^{n}-C_{n+m}^{n-1}.而這種變式的一個經典問題是硬幣找零問題,又n個人有100元,m個人有50元,他們排隊去買東西,而賣家沒有硬幣找零,只能用收的錢找零,問有多少種方案可以使得賣家正常收款。我認爲,100和50其實是一一對應的關係,也就是+1和-1的關係,那麼是不是還可以這樣變式呢?給定n個+2和m個-1,問有多少種排列方法使得前綴和恆>=0?處理方法其實和上面的類似。
  • 再看下一個問題,圓內連弦問題。在一個圓上取2n個點,連出n條弦,問使得這n條弦互不相交的方案數。爲了解決這個問題,我們先引入一個較爲簡單的例子,現在有n個+1 n個-1,需要將他們排列在圓上,使得順時針相加起來任意時刻和都>=0,問有多少種排列方法。這其實和序列上的問題沒什麼區別。那麼現在在符合剛剛要求的排列上,直接將相鄰的+1和-1連起來(連好後兩側的就相鄰),發現剛好是所有弦不相交的。所以連弦問題顯然就是卡特蘭數。
  • 最後還有一個叫卡特蘭問題,實際上就是凸多邊形的剖分,n邊形用n-3條邊分成三角形的方案數就是卡特蘭數。爲了證明這個,需要兩條引理,1.n對括號組成的合法方案數是C(n) 2.n個數連乘的順序有C(n-1)種。括號的很顯然,而第二條引理,實際上是和括號問題建立了一個一一映射的關係,從而得出了結論。具體是n個數,那麼取n-1對括號,對於每一種合法的括號方案,我們設置一個指針指向n個數中的第一個元素,然後順序考慮我們的合法括號序列,遇到左括號,則指針右移一位,遇到右括號,則指針指向的數與其左邊一個數乘起來,將乘出來的結果看成一個數,然後進行完這個過程,所以每種連乘方案和合法括號序列又建立了映射,因此n個數的連乘方案是C(n-1)。而卡特蘭問題就使用了這兩個引理。感興趣的朋友可以參考卡特蘭數 — 計數的映射方法的偉大勝利

注意事項


總結


AC代碼

#include<cstdio>
#include<string>
#include<queue>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxn = 300 + 10;
const int dir[8][2] = { 1, 2, 1, -2, -1, 2, -1, -2, 2, 1, 2, -1, -2, 1, -2, -1 };

void solve()
{
	int n;
	scanf("%d", &n);
	long long dp[100] = { 0 };
	dp[0] = 1;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= i - 1; j++)
			dp[i] += dp[j] * dp[i - j - 1];
		
	}

	printf("%lld\n", dp[n]);
}

void test()
{

}

int main()
{
	freopen("Testin.txt", "r", stdin);
	solve();
	//test();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章