zoukankan      html  css  js  c++  java
  • 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
    勸君惜取少年時&莫待無花空折枝
  • 相关阅读:
    Android基础学习之context
    [原]C语言实现的快速排序,采用分治策略,递归实现
    [原]动态获取应用的视图实际大小
    [原]此程序专用来说明C++模板的用法
    [原]C++程序示例:涉及到抽象类、继承…
    [原]C++关于运算符重载的程序报错error…
    [原]用C#模拟实现扑克牌发牌、排序程序…
    [原]用C#模拟实现扑克牌发牌、排序程序。
    Come on , Android 常用开发工具
    ADB 常用命令
  • 原文地址:https://www.cnblogs.com/RainingDays/p/3078404.html
Copyright © 2011-2022 走看看