题目描述:
题目链接: HDU 1502
Consider words of length 3n over alphabet {A, B, C} . Denote the number of occurences of A in a word a as A(a) , analogously let the number of occurences of B be denoted as B(a), and the number of occurenced of C as C(a) .
Let us call the word w regular if the following conditions are satisfied:
A(w)=B(w)=C(w) ;
if c is a prefix of w , then A(c)>= B(c) >= C(c) .
For example, if n = 2 there are 5 regular words: AABBCC , AABCBC , ABABCC , ABACBC and ABCABC .
Regular words in some sense generalize regular brackets sequences (if we consider two-letter alphabet and put similar conditions on regular words, they represent regular brackets sequences).
Given n , find the number of regular words.
Input
There are mutiple cases in the input file.
Each case contains n (0 <= n <= 60 ).
There is an empty line after each case.
Output
Output the number of regular words of length 3n .
There should be am empty line after each case.
Sample Input
2
3
Sample Output
5
42
题目大意: 有A,B,C三种字母,每个字母有n
(0<n<=60)
个,求由这些字母组成的字母串有多少个满足条件:对于字母串的任意前缀,A的个数大于等于B的个数,B的个数大于等于C的个数。
注意 :输入之间中间空一行,输出中间也要空一行。
题目分析:
这道动态规划题的转移方程并不难想,我们可以令f[i][j][k]记录个数,i代表A的个数,j代表B的个数,k代表C的个数,只要保证i>=j>=k。那么转移方程就是f[i][j][k]=f[i-1][j][k]+f[i][j-1][k]+f[i][j][k-1](如果在方程中i-1
<
<script type="math/tex" id="MathJax-Element-2"><</script>k等也没有关系,因为未更新过它,所以值就是0,并不会影响答案)。
但这道题的关键就在于,最后计算出来的答案到n=60时会非常大(八十几位),于是可以用类似于高精度的方法(压位)处理。对于f数组再开一维,f[i][j][k][p],答案这样记录,f[i][j][k][p+1]=f[i][j][k][p]/10000,f[i][j][k][p]%=10000。也就是说f数组记录的是答案每四位的值,p代表是第几个四位。
附代码:
#include<iomanip>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;
const int INF=1e4;
int n,f[65][65][65][30];
void add(int a[],int b[])
{
for(int i=0;i<=29;i++)
{
a[i]+=b[i];
a[i+1]+=a[i]/INF;
a[i]%=INF;
}
}
int main()
{
//freopen("lx.in","r",stdin);
f[0][0][0][0]=1;
for(int i=1;i<=60;i++)
for(int j=0;j<=i;j++)
for(int k=0;k<=j;k++)
{
add(f[i][j][k],f[i-1][j][k]);
if(j-1>=k) add(f[i][j][k],f[i][j-1][k]);
if(k-1>=0) add(f[i][j][k],f[i][j][k-1]);
}
while(scanf("%d",&n)!=EOF)
{
int k=29;
while(f[n][n][n][k]==0) k--;//找到最高位
for(int i=k;i>=0;i--)
{
if(i!=k)
{
int x=INF/10;
while(f[n][n][n][i]<x&&x>0)//对于非最高位,存在需要填充0的情况
{
printf("0");
x/=10;
}
if(f[n][n][n][i]!=0)printf("%d",f[n][n][n][i]);
}
else printf("%d",f[n][n][n][k]);
}
printf("\n\n");
}
return 0;
}