[HDU 1502][动态规划]Regular Words

题目描述:
题目链接: 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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值