【 OJ 】 HDOJ1023 18年11月08日17:11 [ 22 ]
遲來的正義.....
ummmm,此題不會,全排列爆炸的可能
對於卡特蘭數並沒有理解....
f(n)=f(0)*f(n-1)+f(1)*f(n-2)......f(n-1)*f(0);
---------------------------------------------------------
首先,我們設f(n)=序列個數為n的出棧序列種數。(我們假定,最後出棧的元素為k,顯然,k取不同值時的情況是相互獨立的,也就是求出每種k最後出棧的情況數後可用加法原則,由於k最後出棧,因此,在k入棧之前,比k小的值均出棧,此處情況有f(k-1)種,而之後比k大的值入棧,且都在k之前出棧,因此有f(n-k)種方式,由於比k小和比k大的值入棧出棧情況是相互獨立的,此處可用乘法原則,f(n-k)*f(k-1)種,求和便是Catalan遞迴式。
這題特麼....看錯了,整個思路基於假設【最後出棧的元素為k】,我看成了假設第一個出棧元素是K,那麼f(n-k)可以理解,前面的怎麼也想不出來是怎麼回事為什會是f(k-1)........
此題由於前面的可以推出是卡特蘭數,做時候要用
另類遞推式:
h(n)=h(n-1)*(4*n-2)/(n+1);
本題基本就是得到這個公式然後大數陣列的乘法和除法運算即可.....
有點難受................想到爆炸也想不到的.................
如果說得到了上述的推式,那麼一切都好辦了,做的時候按部就班的算即可,先算(4*n-2)/(n+1) 然後在和h(n-1)做乘法,咋看感覺很好,少了一次大數除法,我開始就是這麼想的,但是當n=3時候 (4*n-2)/(n+1)=2.5..... 2.5和h(n-1)做乘法會丟失資料,是個坑【可能是我並不會小數點和陣列做乘法有關,如果分數可以和陣列的數做乘法那麼這個方法還是比較好的】
AC程式碼如下:
# include<iostream> using namespace std; int C[200][200];//C[n][0]表示n的卡特蘭數的長度,儲存是反向的,C[n][1]表示個位數 void Catalan(void) { C[0][0] = C[0][1] = 1; C[1][0] = C[1][1] = 1; int carry,digit,sum; double t; for (int i = 2; i < 101; i++)//去碰一下 i=2 的坑 { t = (4 * i - 2); carry = sum = 0; //只需要求 h(i-1)並分為儲存 for (int j = 1; j <= C[i - 1][0]; ++j)//C[i-1][0] 為 h(i-1)的長度 { sum = C[i - 1][j] * t + carry; C[i][j] = sum % 10; carry = sum / 10; } digit = C[i - 1][0]; while (carry) {//此時位數開始在原基礎上增加 digit++; C[i][digit] = carry % 10; carry /= 10; } //此時已經求出了 h(n-1)*(4*n-2) 接著 /(n+1); for (int j = digit; j > 0; j--) { sum = C[i][j] + carry * 10; C[i][j] = sum / (i + 1); carry = sum % (i + 1); } while (!C[i][digit]) { digit--; } C[i][0] = digit; } } int main(void) { int N;//火車數 Catalan(); while (cin >> N) {//非整形退出 if (N <= 0) return 0; for (int i = C[N][0]; i > 0; i--) { cout << C[N][i]; } cout << endl; } system("pause"); return 0; }