现在的位置: 首页 > 综合 > 正文

【google apec 2015 1b】problem d: 取第k个括号序列 卡特兰数/动态规划

2018年04月12日 ⁄ 综合 ⁄ 共 3964字 ⁄ 字号 评论关闭

卡特兰数:

 一个2*n的序列,其中每一个元素为+1或-1,

  1. 任意一点左边元素累加和必须>=0.
  2. 最后所有元素加和必须是0
    很容易想到用动态规划来求卡特兰数,优点是方便调试,而且可以定制其中一些参数。
  • 方法一:递推状态和方程:
      a[i][j]为状态,i为当前的串长度。j为当前的累加和(从element[0]加到element[i-1]),显然根据条件1,有j>=0.
      另外有i+j<=len.原因是根据条件2,如果后面的len-i个元素都是-1,则要保证j+(len-i)<=0。否则,显然最后的累加和肯定会>0.
      a[i][j]=  a[i-1][j-1] (当前元素取+1。注意要保证j>0,否则j-1<0越界且不合法) + a[i-1][j+1](当前元素取-1。)
     
    下面实现了采用dp解出 卡特兰剩余序列的种类数。注意delta是什么意思呢,就是起始累加和 j的意思。
  • 方法二:组合公式:
      也能够想到用公式,对一个长度为2*n的卡特兰序列,其计数为C(2*n,n)-C(2*n,n+1).  下面也实现了公式法。

  • 实现:

      下面ca(n,k)实现了组合计数,但没有考虑取模的情况。如果考虑,需要用费马定理。caCatelan(n) 获取长度2*n的卡特兰序列的计数。
      然后dpCatelan用动态规划,计算起始累加和为delta,剩余长度为len的卡特兰剩余序列的计数。优点是取模更方便了
#include <iostream>
#include <vector>
using namespace std;
typedef long long int ll;

// dp to get catelan number
ll dpCatelan(int len,int delta){
  if(delta>len) return 0;//@error: should judge this to prevent over-flow of a[][]
  //@error: specially, when delta==len,the following will all be ')', return 1
  vector<vector<ll> > a(len+1,vector<ll>(len+1,0));
  a[0][delta]=1;//here is very important for katelan number. Delta is the initial sum of catelan remaining series.

  // j=lc-rc+delta>=0 , it is the basic feature of katalan series 
  for(int i=1;i<=len;i++){
    for(int j=0;i+j<=len;j++){
      //j=lc-rc+delta>=0,
      a[i][j]=a[i-1][j+1];
      if(j>0) a[i][j]+=a[i-1][j-1];
    }
  }
  return a[len][0];
}

// another way to cal catelan number
ll ca(int n,int k){
  ll res=1;
  k=k>n-k?n-k:k;
  for(int i=0;i<k;i++){
    //res*=(n-i)/(i+1);//@error: here n-i/i+1 may be 0
    res=res*(n-i)/(i+1);
  }
  return res;
}
ll caCatelan(int n){//@error: this n is the number of pairs, not the same with get(len,delta)'s len
  return ca(2*n,n)-ca(2*n,n+1);
}

int main(){
  for(int n=1;n<30;n++)
    cout<<dpCatelan(2*n,0)<<":"<<caCatelan(n)<<endl;
  return 0;
}
回到本题,长度为2*n的卡特兰括号序列,求第k个。
Problem D. Parentheses Order
An n parentheses sequence consists of n "("s
and 
n ")"s.

A valid parentheses sequence is defined as the following:

You can find a way to repeat erasing adjacent pair of parentheses "()" until it becomes empty.

For example, "(())" is a valid parentheses, you can erase the pair on the 2nd and 3rd position and it becomes "()", then you can make it empty.
")()(" is not a valid parentheses, after you erase the pair on the 2nd and 3rd position, it becomes ")(" and you cannot erase any more.

Now, we have all valid n parentheses sequences. Find the k-th
smallest sequence in lexicographical order.


For example, here are all valid 3 parentheses sequences in lexicographical order:

3
2 2
3 4
3 6

Case #1: ()()
Case #2: ()(())
Case #3: Doesn't Exist!


注意本题大数据很坑爹! 不管采用方法一、二,求卡特兰剩余序列的计数tmp都有可能把long long int爆掉!!!!
幸好md k没有超过long long int,考虑本题的逻辑,当遇到tmp爆掉long long int的时候,只需要返回tmp=-1,然后检查tmp就知道了!
另外,遇到的问题是判断语句中,ll a,b. if(a+b<k), 这个加号+也是分分钟爆掉ll的节奏啊!!果断用减号代替! if(k-a<b),这样就不会ll溢出了!
#include <iostream>
#include <vector>
using namespace std;
typedef long long int ll;
// get the possible catelan series' count 
// in condition when remains len while the sum is currently delta
ll get(int len,int delta){
  if(delta>len) return 0;//@error: should judge this to prevent array-over-index of a[][]
                        //@error: specially when delta==len,the following will all be ')', return 1
  vector<vector<ll> > a(len+1,vector<ll>(len+1,0));
  a[0][delta]=1;
  // j=lc-rc+delta>=0  
  for(int i=1;i<=len;i++){
    for(int j=0;i+j<=len;j++){
      //j=lc-rc+delta>=0,
      a[i][j]=a[i-1][j+1];
      if(a[i][j]<0){ a[i][j]=-1;continue;}// @error: the result can be overflow! since catelan[100] is really big! so use -1 as overflow flag here!
      if(j>0) {
        a[i][j]+=a[i-1][j-1];
        if(a[i][j]<0){ a[i][j]=-1;continue;}//overflow flag as above
      }
    }
  }
  //if(a[len][0]<0) cout<<"overflow in catelan[len,delta]!!!"<<endl;
  return a[len][0];
}
bool loop(int n,ll k,char *buf){
  if(n==0||n%2) return false;
  int lc=0,rc=0;//
  ll cnt=0;
  for(int i=0;i<n;i++){
    buf[i]='(';
    lc++;
    ll tmp=get(n-1-i,lc-rc);
    //cout<<n-1-i<<":"<<lc-rc<<":"<<cnt<<":"<<tmp<<endl;

    //if(cnt+tmp<k) {//@error: be careful for every operator, especially when using long long type
    // here tmp can be very large! so big data should always care for int-or-ll-overflow
    if(tmp>=0&&tmp<k-cnt){//@error: as solution, avoid using +, instead using - in case of overflow !  and tmp >=0 means no overflow in tmp!
      buf[i]=')';
      lc-=1,rc+=1;
      if(lc<rc) return false;
      cnt+=tmp;
    }
  }
  buf[n]=0;
  return true;
}


int main(){
    int n;cin>>n;char buf[1000];
    for(int i=0;i<n;i++){
      int N; ll K;cin>>N>>K; //@error: k should be long long int
      cout<<"Case #"<<i+1<<": ";
      if(loop(2*N,K,buf)){
        cout<<buf<<endl;
      }
      else cout<<"Doesn't Exist!"<<endl;
    }
    return 0;
}

抱歉!评论已关闭.