题目我读了好久~~~
大概题意:给一个只有左右括号的字符序列,这个序列不一定是题目中说的平衡序列,然后让用H,G两种字符来标记这个序列,使得从左向右看的时候只看H,所有标记H的括号可以组成一个平衡序列,然后从右向左看的时候只看G标记的括号,G标记的括号也组成一个平衡序列,让输出的就是这种标记方案的总数。
首先序列的长度肯定是一个偶数,我们可以这么想,因为要使H,G构成的序列都是平衡序列,那么他俩都应该是偶数的,而且题目不允许存在没有被标记的字符,所以偶数+偶数必然是偶数。
这题我最多想到的就是什么暴力搜索的想法,但是这么大根本没法承受。。无奈寻求题解帮助。我最近碰到一种人,就是不看题解的人。不想说了。下面我说说自己对题解的想法:
官方题解:http://www.usaco.org/current/data/sol_bbreeds.html
题解先是给咱提供了一个n^3的动态规划想法,然后又优化到了n^2.先说复杂度较高的那个,第二段开始他给咱定义了一个状态f(i,A_open,B_open),指把s_i...s_n这段标记完总共有多少可以使得整个序列平衡的方案数,换句话就是说,s_i...s_n这部分如果用H,G来标记,是不是2^X这么多种方案,那个f是这些方案中的一些,并且是配合s_1...s_i-1使得序列平衡的那些。然后又说,如果当前s[i]=='(',那么f(i,A_open,B_open)=f(i+1,A_open+1,B_open)+f(i+1,A_open,B_open+1),这个转移是这样的,sorry,说到现在发现前面忘解释A_open,B_open的意义了,A_open+B_open是前i-1中左括号的个数和,比如前i-1中一共有3个左括号,那么(0,3),(1,2),(2,1),(3,0)都可以是A_open,B_open的合法的分配方案。嗯。接着说那个转移的意思,如果当前s[i]=='(',那么我们可以把状态转移到i+1去,又因为是左括号,所以我们可以让A_open,B_open两者中的一个+1,就是在之前A_open,B_open的分配的基础上,分配方案发生了变化,或者说是又多了一部分。那很自然f(i,A_open,B_open)必然是两者之和。并且可以把i这个地方标记成H,或者G,也就是题解中说的A,B。如果当前s[i]==')',那么如果A_open>0,则我们把当前位置i标记成A,如果B_open>0,则我们把当前位置标记成B,看到现在我们看明白了,A_open,B_open分别对应着标记A,B。
基础条件i=n,因为我们在处理序列的过程中并没有违背什么约束条件,这里指的约束条件我猜他是指无后效性什么的吧。假如左括号个数等于右括号个数,所以我们最终肯定会得到两个平衡序列,他这里指的两个是指我上面题意中说的从左向右看,从右向左看~~,因此我们让开始状态f(n,0,0)=1。
因为n^3的算法对于1000的数据量实在是太大了,并且我们注意到A_open,B_open之间的关系(我上文中有提到)。所以可以只要记录(i, A_open)就可以达到目的,这样便成功的优化到了n^2。至此,题解翻译完毕,我们对A_open,B_open的认识更近了一步,他俩分别表示前i-1项中,分别标记了A和B的左括号的个数。这样也可以适当的解释一下为什么f(n,0,0)=1了。
我不知道上文中有没有把我想要表达我的第一理解意思表达出来,请大家见谅。
官方题解中的java代码我想大家应该很容易就能看懂,至于c++的代码,我也看不懂,并且手动模拟了一下,还是没懂。求看懂的人相告。
============================================================2013.5.29更
刚刚又回过头来看了看这个题,发现c++的代码也仿佛能看懂了。至于题解中java的记忆化搜索相信大家很容易就能看懂了。至此,此题便明了额。下面把官方题解贴过来,以防以后链接失效。
java代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import java.util.*; import java.io.*; import java.awt.Point; import static java.lang.Math.*; public class bbreeds { static int n; static char[] S; static int[] O; static void check(boolean b) { if(!b) throw new RuntimeException("data invalid"); } public static void main(String[] args) throws Exception { Scanner in = new Scanner(new File("bbreeds.in")); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("bbreeds.out"))); S = in.next().toCharArray(); n = S.length; check(n <= 1000); for(int i=0; i<n; i++) check(S[i]=='(' || S[i]==')'); O = new int[n+1]; dp = new int[n][n]; for(int i=0; i<n; i++) for(int j=0; j<n; j++) dp[i][j] = -1; O[0] = 0; for(int i=0; i<n; i++) O[i+1] = O[i] + (S[i]=='('?1:-1); out.println(f(0, 0)); out.flush(); } static int[][] dp; static int f(int i, int A) { if(i == n) return 1; if(dp[i][A] >= 0) return dp[i][A]; int B = O[i] - A; if(S[i] == '(') return dp[i][A] = (f(i+1,A+1)+f(i+1,A))%2012; else { int ans = 0; if(A > 0) ans += f(i+1, A-1); if(B > 0) ans += f(i+1, A); return dp[i][A] = ans%2012; } } }
c++代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <vector> #include <cstring> #include <cstdio> using namespace std; #define MOD 2012 #define MAXN 1010 int A[MAXN]; int main() { freopen("bbreeds.in", "r", stdin); freopen("bbreeds.out", "w", stdout); int L = A[1] = 1; for(int ch = cin.get(); L > 0 && ch == '(' || ch == ')'; ch = cin.get()) { int dir = ch == '(' ? 1 : -1; L += dir; for(int j = dir < 0 ? 1 : L; 1 <= j && j <= L; j -= dir) { A[j] += A[j - dir]; if(A[j] >= MOD) A[j] -= MOD; } A[L + 1] = 0; } cout << (L == 1 ? A[1] : 0) << endl; }