您的位置:首页 > 其它

Uva 10157 - Expressions 解题报告(递推)

2014-04-02 09:39 387 查看
Problem A: EXPRESSIONS 

Let X be the set of correctly built parenthesis expressions. The elements of X are strings consisting only of the characters �(� and �)�. The set X is defined as follows:
an empty string belongs to X
if A belongs to X, then (A) belongs to X
if both A and B belong to X, then the concatenation AB belongs to X.
For example, the following strings are correctly built parenthesis expressions (and therefore belong to the set X):
()(())()
(()(()))
The expressions below are not correctly built parenthesis expressions (and are thus not in X):
(()))(()
())(()
Let E be a correctly built parenthesis expression (therefore E is a string belonging to X).
The length of E is the number of single parenthesis (characters) in E.
The depth D(E) of E is defined as follows:
               ì 0 if E is empty 
D(E)= í D(A)+1 if E = (A), and A is in X 
               î max(D(A),D(B)) if E = AB, and A, B are in X
For example, the length of �()(())()� is 8, and its depth is 2.
What is the number of correctly built parenthesis expressions of length n and depth d, for given positive integers n and d?
Task 

Write a program which
reads two integers n and d
computes the number of correctly built parenthesis expressions of length n and depth d;
Input data 
Input consists of lines of pairs of two integers - n and d, at most one pair on line, 2 £ n £ 300, 1 £ d £ 150. 
The number of lines in the input file is at most 20, the input may contain empty lines, which you don't need to consider.
Output data 

For every pair of integers in the input write single integer on one line - the number of correctly built parenthesis expressions of length n and depth d.
Example 

Input data                                   Output data 
6 2                 3 
300 150             1 

There are exactly three correctly built parenthesis expressions of length 6 and depth 2: 
(())() 
()(()) 
(()()) 

    解题报告:挺艰难的一题。
    首先,我们分析一下题意:求指定长度和深度的括号序列的种数。看到括号序列,最先想到的就是之前《训练指南》上串并联网络那题。这题也可以很方便的划分为树形。指定整个串有个虚根,连接表示为兄弟关系,嵌套表示为父子关系。那么这题就可以表达为求节点数为n/2+1,深度为k+1的树的种数。(())()序列可以表示为下图:



    如何递推这个问题的结果呢?当时笔者就卡在这个地方。直接求节点数m=n/2+1,深度为k+1的树的种数还是比较困难的。我们可以枚举第一个儿子所形成的子树的种数和剩下部分树的种数。只要其中一部分的深度等于k+1即可。这样就有三种情况:左边子树深度为k+1,剩下部分深度为k+1;左边深度不够,右边深度满足;左边深度满足,右边深度不够。深度为k+1还可以递推,深度不足则需要枚举可能的深度,复杂度就高了。



    笔者就卡在这里了。看了一下别人的解题报告= =。
    原来可以这样,每次求节点为m,深度小于等于k+1的子树的数量,再减去节点为m,深度小于k+1的子树的数量,那么得到的必是所求。
    思路肯定是对的。后来遇到的问题就是大数模版的问题。以前用的别人的大数模板运算减法时除了问题。花了很长时间完成了自己的大数模版,完美支持负数和减法。今天早上提交一发,就A了。
    代码如下:
/*
* 大数模版
* 完美支持负数及加减法操作
* 支持乘法,大小比较,赋值
* 支持与long long类型的赋值比较
* SF-_- 14.04.01
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int K = 10000; // 数组里每位代表1W
const int M = 30; // 一共10位
const char show[] = "%04lld";

struct Bignum
{
LL a[M*2]; // 大数数组
int len; // 长度
bool negative; // 正负

Bignum()
{
clear();
}

void clear()
{
len=0;
negative=false;
memset(a, 0, sizeof(a));
}

Bignum(LL num)
{
*this=num;
}

Bignum operator=(LL num)
{
clear();
if(num<0) negative=true, num=-num;
while(num)
a[len++]=num%K,num/=K;
return *this;
}

Bignum(const Bignum& cmp)
{
memcpy(this, &cmp, sizeof(Bignum));
}

Bignum operator=(const Bignum& cmp)
{
memcpy(this, &cmp, sizeof(Bignum));
return *this;
}

int absCmp(const Bignum& cmp)
{
if(len!=cmp.len)
return len>cmp.len?1:-1;

for(int i=len-1;i>=0;i--)
if(a[i]!=cmp.a[i])
return a[i]>cmp.a[i]?1:-1;

return 0;
}

int absCmp(LL num)
{
Bignum cmp(num);
return absCmp(cmp);
}

bool operator<(const Bignum& cmp)
{
if(negative^cmp.negative)
return negative?true:false;

if(negative)
return absCmp(cmp)>0;
else
return absCmp(cmp)<0;
}

bool operator<(LL num)
{
Bignum cmp(num);
return *this<cmp;
}

bool operator==(const Bignum& cmp)
{
if(negative^cmp.negative)
return false;
return absCmp(cmp)==0;
}

bool operator==(LL num)
{
Bignum cmp(num);
return *this==cmp;
}

void absAdd(const Bignum& one, const Bignum& two)
{
len=max(one.len, two.len);
for(int i=0;i<len;i++)
{
a[i]+=one.a[i]+two.a[i];
if(a[i]>=K) a[i]-=K, a[i+1]++;
}
if(a[len]) len++;
}

void absSub(const Bignum& one, const Bignum& two)
{
len=one.len;
for(int i=0;i<len;i++)
{
a[i]+=one.a[i]-two.a[i];
if(a[i]<0) a[i+1]--,a[i]+=K;
}
while(len>0 && a[len-1]==0) len--;
}

void absMul(const Bignum& one, const Bignum& two)
{
len=one.len+two.len;
for(int i=0;i<one.len;i++) for(int j=0;j<two.len;j++)
a[i+j]+=one.a[i]*two.a[j];
for(int i=0;i<len;i++) if(a[i]>=K)
a[i+1]+=a[i]/K,a[i]%=K;
while(len>0 && a[len-1]==0) len--;
}

Bignum operator+(const Bignum& cmp)
{
Bignum c;
if(negative^cmp.negative)
{
bool res = absCmp(cmp)>0;
c.negative = !(negative^res);
if(res)
c.absSub(*this, cmp);
else
c.absSub(cmp, *this);
}
else if(negative)
{
c.negative=true;
c.absAdd(*this, cmp);
}
else
{
c.absAdd(*this, cmp);
}
return c;
}

Bignum operator-(const Bignum& cmp)
{
Bignum cpy;
if(cpy==cmp)
return *this;
else
cpy=cmp, cpy.negative^=true;

return *this+cpy;
}

Bignum operator*(const Bignum& cmp)
{
Bignum c;
if(c==cmp || c==*this)
return c;

c.negative = negative^cmp.negative;
c.absMul(*this, cmp);
return c;
}

void output()
{
if(len==0)
{
puts("0");
return;
}

if(negative)
printf("-");

printf("%lld", a[len-1]);
for(int i=len-2;i>=0;i--)
printf(show, a[i]);
puts("");
}
};

Bignum dp[155][155];

Bignum dfs(int n, int k)
{
if(n==1) return 1;
if(k==1) return 0;

Bignum &ans = dp
[k];
if(!(ans==-1)) return ans;
ans=0;

for(int i=1;i<n;i++)
ans=ans+dfs(i, k-1)*dfs(n-i, k);
return ans;
}

void init()
{
for(int i=0;i<155;i++)
for(int j=0;j<155;j++)
dp[i][j]=-1;
}

int main()
{
init();

int n,k;
while(~scanf("%d%d", &n, &k))
{
if(n&1)
puts("0");
else
(dfs(n/2+1, k+1)-dfs(n/2+1, k)).output();
}
}
    大数模版这东西,最好自己写一个,或者完全将别人的读懂。经常用,发现问题及时修改。无论是网络赛还是正式比赛,有个靠谱的大数模版总是好的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息