usaco Balanced Cow Breeds

  题目我读了好久~~~

  大概题意:给一个只有左右括号的字符序列,这个序列不一定是题目中说的平衡序列,然后让用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代码:

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;
        }
    }
}
View Code

c++代码:

#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;
}
View Code

转载于:https://www.cnblogs.com/RainingDays/archive/2013/05/15/3078404.html

猜你喜欢

转载自blog.csdn.net/weixin_33734785/article/details/94538692