【线性DP】——hdu1502——渐进推理+大数

                                   Regular Words

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1982    Accepted Submission(s): 765


Problem Description
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个A,n个B,n个C的时候,满足组成的字符串的所有前缀A的个数大于等于B的个数大于等于C的个数,并B的个数大于等于C的个数有多少个?

 

思路:

这道题的dp有点难想,从题目的字面上理解:
当i=1时, ans= ABC
当i=2时, ans= AABBCC AABCBC ABABCC ABACBC ABCABC
其实不难发现,若集合ans中的任一子串的前缀满足A(C)>=B(C)>=C(C)那么一定有第一个B前面必有A,第一个C前面必有B,第二个B前面必有两个A,第二个C前面必有两个B………………
不难推出,当i=n时的所有满足要求的串为i=n-1的所有满足要求的子串中在任意位置插入'A','B','C',A必插入在B之前,B必插入在C之前,
然后就是想如何在i=n-1的所有串中不重不漏地插入ABC,得出i=n的值,但这样的话好像找不到什么可以解决的方法。。。 那么我们可不可以不一下子地从ans1={ABC}跳到ans2={AABBCC,AABCBC,ABABCC....},
先从{ABC}跳到{ABCA,ABCB,ABCC}这样行不行?那么就从加入ABC的问题变为加入A或B或C的问题了,
就相当于求i=n时在i=n-1上的子串结尾加入A或B或C,然后挑出最后满足要求的子串,好,AC!
转移方程是dp[i][j][k]=dp[i-1][j][k]+dp[i][j-1][k]+dp[i][j][k-1];(i>j>k)

代码如下:
import java.util.*;
import java.math.*;
public class Main{
    public static void main(String[] args){
        Scanner cin=new Scanner(System.in);
        BigInteger dp[][][]=new BigInteger[61][61][61];
        dp[0][0][0]=BigInteger.valueOf(1);
        for(int i=1;i<=60;i++)
            for(int j=0;j<=i;j++)
                for(int k=0;k<=j;k++){
                    dp[i][j][k]=BigInteger.valueOf(0);
                    if(i-1>=j)dp[i][j][k]=dp[i][j][k].add(dp[i-1][j][k]);
                    if(j-1>=k)dp[i][j][k]=dp[i][j][k].add(dp[i][j-1][k]);
                    if(k>0)dp[i][j][k]=dp[i][j][k].add(dp[i][j][k-1]);
                }
        while(cin.hasNext()){
            int n=cin.nextInt();
            System.out.println(dp[n][n][n]);
            System.out.println();
        }
        cin.close();
    }
}

题后感:

当题目给出的状态是离散的,

那么可以不考虑怎么从离散的相邻状态转移,

而可以用题目隐含的,真实存在的“过渡状态”使状态变为连续的

posted @ 2016-07-22 07:52  琥珀川||雨露晨曦  阅读(99)  评论(0)    收藏  举报