ABC167F Bracket Sequencing

解法类型:贪心排序,对称性。

题目链接

一个括号序列 \(S\) 对应着一个二元组 \((x,y)\),表示 \(S\) 中有 \(x\) 个未配对的右括号“)”,有 \(y\) 个未配对的左括号“(”。例如,((( 对应二元组 \((0,3)\))( 对应二元组 \((1,1)\)()) 对应二元组 \((1,0)\)

以下我们转而考虑括号序列对应的二元组。

能构成平衡的二元组序列的必要条件是 \(\sum_{i=1}^{n} x_i = \sum_{i=1}^{n} y_i\)

\(n\) 个二元组的某个排列 \(p_1, p_2, \dots, p_n\) 是平衡的当且仅当
对于 \(i = 1, 2, \dots, n\)
\begin{equation}
x_{p_i} \le \sum_{j=1}^{i-1} y_{p_j} - x_{p_j} \label{condition}\tag{\(\bigstar\)}
\end{equation}

\(n\) 个二元组划分成两部分 \(B_1\), \(B_2\),第一部分满足 \(x \le y\),第二部分满足 \(x > y\)

若有解,则一定存在一个解使得 \(B_1\)\(B_2\) 之前。

事实上,若有解则按下述方法构造出的二元组序列一定是平衡的:
\(B_1\) 中的二元组按 \(x\) 从小到大排序,将 \(B_2\) 中的二元组按 \(y\) 从大到小排序。

可以通过对称性来解释这个算法的正确性:
我们把上面提到的分组方法改一下:将 \(n\) 个二元组分成三组 \(B_1 := \\{(x,y): x < y\\}\)\(B_2 := \\{(x,y): x > y\\}\),$B_3 := \{(x,y) : x = y\} $。

依据直觉我们可以按下述方法排列给定的 \(n\) 个二元组

  1. \(B_1,B_3, B_2\) 的顺序排列。
  2. \(B_1\) 里的二元组按 \(x\) 从小到大排列,检查如此排列是否满足条件 \eqref{condition},若不满足则无解。
  3. \(\max\\{x : (x,x)\in B_3\\} > \sum_{(x,y)\in B_1}y - x\),则无解,否则将 \(B_3\) 里的二元组以任意顺序排列。
  4. 考虑如何排列 \(B_2\)

考虑把给定的 \(n\) 个括号序列反转,也就是先把左括号变成右括号再把头尾反转。例如()(( 变成 ))(),这样对应的二元组由 \((x,y)\) 变为 \((y,x)\)。显然变换之前有解当且仅当变换之后有解。变换之后,\(B_3\) 不变,\(B_1, B_2\) 恰好“角色互换”:将变换之后的 \(B_1, B_2\) 记作 \(B'_1, B'_2\),把 \(B'_1\) 里的二元组的 \(x,y\) 互换即得到 \(B_2\),把 \(B'_2\) 里的二元组的 \(x,y\) 互换即得到 \(B_1\)

不难看出,给定的 \(n\) 个括号序列能排列成平衡的括号序列当且仅当 \(B_1, B'_1\) 都满足上述第 2 个条件且 \(B_3\) 满足上述第 3 个条件。

  void solve() {
    auto check = [](vector<pair<int,int>>& p) {
      sort(p.begin(), p.end(), [](auto& a, auto& b){return a.first < b.first;});
      int n_left = 0;
      for (auto x : p) {
        if (x.first > n_left) return -1;
        n_left += x.second - x.first;
      }
      return n_left;
    };
    int n;
    cin >> n;
    vector<pair<int,int>> B1, B2;
    int mx = 0;
    int need = 0, give = 0;
    for (int i = 0; i < n; ++i) {
      string s;
      cin >> s;
      int need_l = 0, give_l = 0;
      for (auto x : s) {
        if (x == '(') ++give_l;
        else {
          if (give_l > 0) --give_l;
          else ++need_l;
        }
      }
      need += need_l;
      give += give_l;
      if (need_l == give_l) mx = max(need_l, mx);
      else if (need_l < give_l) B1.emplace_back(need_l, give_l);
      else B2.emplace_back(give_l, need_l);
    }
    cout << (need == give and mx <= check(B1) and check(B2) >= 0 ? "Yes" : "No") << '\n';
  }
posted @ 2020-05-11 22:10  Pat  阅读(309)  评论(0编辑  收藏  举报