标准二维表问题

问题描述:

设n 是一个正整数。2xn的标准2维表是由正整数1,2,…,2n 组成的2xn 数组,该数组的每行从左到右递增,每列从上到下递增。2xn的标准2维表全体记为Tab(n)。

例如,当n=3时Tab(3)如下:

思路分析:首先明确一下每行的数总是左边小于后面,上面小于下面,以上面的第一种情况进行分析,我们把第一行的数字对应为1表示进栈

。第二行的数字对应为-1表示出栈。我们知道一般情况进栈和出栈时栈里面的元素个数大于等于0,那么数字1看成进栈-1看成出栈,则总数之和要大于等于0。

即进出栈操作任何时刻进栈次数大于等于出栈的次数。那么上表的第一行第一个元素表示第一次是进栈操作,下面的4对应的是第一次进栈的元素在第四次出栈,第一行第二列2

表示第二次操作是进栈对应对应下面的5表示第五次操作是出栈,即把第二次进栈操作的元素出栈,依次类推,第三次是进栈操作,第六次把第三次进栈的元素弹出栈。

所以我们可以把表看出是元素的进出栈的操作,则tab(n)表示求元素个数为n的所有可能进出栈的操作。于是问题转换为n个元素所有可能进出栈的情况。

而求进出栈的所有可能情况的方法就是卡特兰数。下面简单介绍一下卡特兰数。

事实上,可以认为问题是,任意两种操作,要求每种操作的总次数一样,且进行第k次操作2前必须先进行至少k次操作1。我们假设一个人在原点,操作1是此人沿右上角45°走一个单位(一个单位设为根号2,这样他第一次进行操作1就刚好走到(1,1)点),操作2是此人沿右下角45°走一个单位。第k次操作2前必须先进行至少k次操作1,就是说明所走出来的折线不能跨越x轴走到y=-1这条线上!在进行n次操作1和n此操作2后,此人必将到到达(2n,0)!若无跨越x轴的限制,折线的种数将为C(2n,n),即在2n次操作中选出n次作为操作1的方法数。

现在只要减去跨越了x轴的情况数。对于任意跨越x轴的情况,必有将与y=-1相交。找出第一个与y=-1相交的点k,将k点以右的折线根据y=-1对称(即操作1与操作2互换了)。可以发现终点最终都会从(2n,0)对称到(2n,-2)。由于对称总是能进行的,且是可逆的。我们可以得出所有跨越了x轴的折线总数是与从(0,0)到(2n,-2)的折线总数。而后者的操作2比操作1要多0-(-2)=2次。即操作1为n-1,操作2为n+1。总数为C(2n,n-1)。

catalan数和出栈序列的对应:

动态规划:我们把n个元素的出栈个数的记为f(n), 那么对于1,2,3, 我们很容易得出:

f(1) = 1     //即 1

f(2) = 2    //即 12、21

f(3) = 5     //即 123、132、213、321、231

然后我们来考虑f(4), 我们给4个元素编号为a,b,c,d, 那么考虑:元素a只可能出现在1号位置,2号位置,3号位置和4号位置(很容易理解,一共就4个位置,比如abcd,元素a就在1号位置)。

分析:

1) 如果元素a在1号位置,那么只可能a进栈,马上出栈,此时还剩元素b、c、d等待操作,就是子问题f(3);

2) 如果元素a在2号位置,那么一定有一个元素比a先出栈,即有f(1)种可能顺序(只能是b),还剩c、d,即f(2),     根据乘法原理,一共的顺序个数为f(1) * f(2);

3) 如果元素a在3号位置,那么一定有两个元素比1先出栈,即有f(2)种可能顺序(只能是b、c),还剩d,即f(1),

根据乘法原理,一共的顺序个数为f(2) * f(1);

4) 如果元素a在4号位置,那么一定是a先进栈,最后出栈,那么元素b、c、d的出栈顺序即是此小问题的解,即         f(3);

结合所有情况,即f(4) = f(3) + f(2) * f(1) + f(1) * f(2) + f(3);

为了规整化,我们定义f(0) = 1;于是f(4)可以重新写为:

f(4) = f(0)*f(3) + f(1)*f(2) + f(2) * f(1) + f(3)*f(0)

然后我们推广到n,推广思路和n=4时完全一样,于是我们可以得到:

f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)

下面是推导式,不给证明了

另类递推式:

h(n)=h(n-1)*(4*n-2)/(n+1);

递推关系的解为:

h(n)=C(2n,n)/(n+1) (n=0,1,2,...)

递推关系的另类解为:

h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)

那么我们了解了卡特兰数之后就可以写代码了,demo如下

#include<bits/stdc++.h>

using namespace std;
int tan(int n){
if(n==1|| n==0)
return 1;
else if(n==2)
return 2;
else {
return tan(n-1)*(4*n-2)/(n+1);
}

}
int main()
{
int n;
cin >> n;
cout << tan(n);
return 0;
}

但是这个代码只是简单运用了卡特兰数解决了n较小的问题,但是如下

其前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...

所以n较大的时候Int就无法表示,下面是处理大整数后的思路(关于大整数的内容可以我的参考这篇博客(https://www.cnblogs.com/henuliulei/p/9867127.html))

下面是具体的大卡特兰数

#include<bits/stdc++.h>
using namespace std;
char array1[200];
char array2[200];
int sum[400];
string f(string s1,string s2)//大整数乘法
{
reverse(s1.begin(),s1.end());
reverse(s2.begin(),s2.end());
memset(array1,0,sizeof(s1));
memset(array2,0,sizeof(s2));
memset(sum,0,sizeof(sum));
int l1=s1.length();
int l2=s2.length();
strcpy(array1,s1.c_str());
strcpy(array2,s2.c_str());
for(int i=0;i<l1;i++){
for(int j=0;j<l2;j++){
sum[i+j]+=((int)array1[i]-48)*((int)array2[j]-48);
}
}
int f=0;
while(true){

if(f<l1+l2){
int de=sum[f]/10;
sum[f]=sum[f]%10;
sum[f+1]+=de;
f++;
}
else{
break;
}
}
string a="";
int ig=0;
for(int i=l1+l2-1;i>=0;i--){
if((char)sum[i]+48!=‘0‘){
ig=1;
}
if(ig==1){
a+=(char)sum[i]+48;
}
}
return a;
}
string div(string a,int b){//大整数除法

char s[1000];
memset(s,0,sizeof(s));
unsigned long long sum=0;
for(int i=0;a[i];i++)
{
sum=sum*10+a[i]-‘0‘;
s[i]=sum/b+‘0‘;
sum=sum%b;
}
int j=0;
string as="";
while(s[j]==‘0‘)
j++;

for(;j<a.size();j++)
as+=s[j];//商
return as;

}
int t(string n)//把字符串变为int类型
{
int a=0;
int s=1;
for(int i=n.length()-1;i>=0;i--){
a+=((int)n[i]-48)*(s);
s*=10;
}
return a;
}
string tan(string n){//递归实现卡特兰数
if(n=="1"|| n=="0")
return "1";
else if(n=="2")
return "2";
else {
int s=t(n);
s=(s+1);
int st=t(n)*4-2;
int s1=t(n);
s1--;
char a[100];
char a1[100];
itoa(st,a,10);
itoa(s1,a1,10);
string s2=tan(a1);
string re=div(f(s2,a),s);
return re;
}
}
int main()
{
string s1;
string s2;
string s3;
cin >> s1;
cout << tan(s1);
}

原文地址:https://www.cnblogs.com/henuliulei/p/9867106.html

时间: 2024-11-07 08:17:24

标准二维表问题的相关文章

全军出击,银联出台标准二维码产品

在很久很久之前,有一个叫做"外星人"的人,发出了一声振聋发聩的呐喊,"如果银行不改变,我们就改变银行!"在当时,这句让无数人嗤之以鼻并评论为"螳臂当车"的话,竟然,真的实现了. 几天前,中国银联在北京宣布,正式推出银联标准二维码产品,并且获得四十多家商业银行的共同支持,表现为二维码支付已经正式进入互联互通的时代. 之前一直说的银行要进入移动支付市场,也被报道过会有各种动作,但像这次一样一下子被40多家银行支持的大事件还是有点出乎意料的感觉,并且据

js 标准二维数组变一维数组的方法

问题:[[0, 1], [2, 3], [4, 5]] -> [0, 1, 2, 3, 4, 5]? 方法一 利用es5的arr.reduce(callback[, initialValue])实现 var arr1 = [[0, 1], [2, 3], [4, 5]]; var arr2 = arr1.reduce(function (a, b) { return a.concat(b)} ); // arr2 [0, 1, 2, 3, 4, 5] 方法二 利用apply实现 var arr1

web项目开发 之 前端规范 --- JSON数据传输规范

此文严格按照W3C规范和部分实际项目可读性,浏览器加载,性能等众多属性权衡,做出平时前端编码规范 文档.供广大web工作者参考并实施,对维护和项目扩展升级都能省时省力. 场景:前后端分离 异步利用JSON 传输数据: E-JSON数据传输标准 简介 E-JSON的设计目标是使业务系统向浏览器端传递的JSON数据保持一致,容易被理解和处理,并兼顾传输的数据量.E-JSON依托于http协议(rfc2616)与JSON数据交换格式(rfc4627). JSON数据类型 JSON(JavaScript

数据挖掘中所需的概率论与数理统计知识

http://blog.csdn.net/v_july_v/article/details/8308762 数据挖掘中所需的概率论与数理统计知识 (关键词:微积分.概率分布.期望.方差.协方差.数理统计简史.大数定律.中心极限定理.正态分布) 导言:本文从微积分相关概念,梳理到概率论与数理统计中的相关知识,但本文之压轴戏在本文第4节(彻底颠覆以前读书时大学课本灌输给你的观念,一探正态分布之神秘芳踪,知晓其前后发明历史由来),相信,每一个学过概率论与数理统计的朋友都有必要了解数理统计学简史,因为,

实用chrome插件

2015年最实用的9款chrome插件 随着14年chrome浏览器的市场超过IE浏览器,chrome凭借它强劲性能和出色的使用体验真正的登上了平民级的殿堂.今天小编就为大家推荐9款自己常用的chrome插件神器. 1.非常实用的chrome新标签页---Infinity新标签页 Infinity新标签页插件是一款可以把chrome默认新标签页换成一个美观实用的infinity新标签页,不仅有简洁美观的页面,还有快速拨号,邮件提醒,天气预报,笔记功能,待办事项,壁纸,历史记录管理等. 2.Chr

推荐系统中所需的概率论与数理统计知识

前言 一个月余前,在微博上感慨道,不知日后是否有无机会搞DM,微博上的朋友只看不发的围脖评论道:算法研究领域,那里要的是数学,你可以深入学习数学,将算法普及当兴趣.想想,甚合我意.自此,便从rickjin写的"正态分布的前世今生"开始研习数学. 如之前微博上所说,"今年5月接触DM,循序学习决策树.贝叶斯,SVM.KNN,感数学功底不足,遂补数学,从'正态分布的前后今生'中感到数学史有趣,故买本微积分概念发展史读,在叹服前人伟大的创造之余,感微积分概念模糊,复习高等数学上册,

机器学习数学基础

数据挖掘中所需的概率论与数理统计知识 (关键词:微积分.概率分布.期望.方差.协方差.数理统计简史.大数定律.中心极限定理.正态分布) 导言:本文从微积分相关概念,梳理到概率论与数理统计中的相关知识,但本文之压轴戏在本文第4节(彻底颠覆以前读书时大学课本灌输给你的观念,一探正态分布之神秘芳踪,知晓其前后发明历史由来),相信,每一个学过概率论与数理统计的朋友都有必要了解数理统计学简史,因为,只有了解各个定理.公式的发明历史,演进历程.相关联系,才能更好的理解你眼前所见到的知识,才能更好的运用之.

中文输入程序

ChineseInput.c #include <stdio.h> #include <string.h> #include "PY_Index.h" unsigned char * search_Chinese(char *buf) { static char str[50]={""}; int i=0; strcpy(str,"PY_mb_"); strcat(str,buf); for(i=0;i<447;i+

HDU 3535 AreYouBusy (混合背包)

题意:给你n组物品和自己有的价值s,每组有l个物品和有一种类型: 0:此组中最少选择一个 1:此组中最多选择一个 2:此组随便选 每种物品有两个值:是需要价值ci,可获得乐趣gi 问在满足条件的情况下,可以得到的最大的乐趣是多少,如果不能满足条件就输出-1 题解:二维01背包 dp[i][j]:前i组物品我们拥有j的价值时最大可获得的乐趣 0:我们需要先把dp[i]所有赋值为负无穷,这样就只能最少选一个才能改变负无穷 1:我们不需要:dp[i][j-ci]+gi(在此组中再选一个),这样就一定最