CF1598F - RBS (状压dp)

题目

\(n\)个括号序列,可以对这些序列任意排列,然后连接成一整个括号序列。求一个排列,使得连接成的括号序列的真前缀是合法括号序列的个数最多。\(n\le 20\)

题解

观察性质,发现括号序列的balance一旦小于0,后面无论是什么都不会使得当前前缀的合法。合法括号序列的前缀的个数即balance为0且前面balance都大于等于0的个数。

显然看到\(n\)那么小,就想到枚举串。设\(f(i,j)\)为选择\(i\)的字符串集合(状压)作为前缀,\(j\)代表这个前缀是否合法,的最多的个数。每次枚举在前缀后面添加什么字符串,会对答案产生贡献。由于知道\(i\)的字符串集合,就知道当前前缀结尾的balance。预处理出添加字符串,前面的balance的多少时对答案的贡献即可。然后转移就很容易。预处理用一个指针移动O(n)即可处理出来。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 2e6 + 10;
const double eps = 1e-5;

bool fg[30][N];
int val[30][N];
int dp[N][2];
bool ok[N];

vector<vector<int>> bal;

int calbal(int m) {
    int res = 0;
    int cur = 0;
    while(m) {
        if(m & 1)
            res += bal[cur].back();
        cur++;
        m >>= 1;
    }
    return res;
}

int main() {
    IOS;
    int n;
    cin >> n;
    int tot = 0;
    for(int i = 1; i <= n; i++) {
        string t;
        cin >> t;
        vector<int> bt(t.size());
        tot += t.size();
        for(int j = 0; j < bt.size(); j++) {
            bt[j] = (t[j] == '(') ? 1 : -1;
            if(j > 0) bt[j] += bt[j - 1]; 
        }
        bal.push_back(bt);
    }
    for(int i = 0; i < n; i++) {
        vector<int> &tar = bal[i];
        int mi = tar.front();
        for(int j = 0; j < tar.size(); j++) {
            mi = min(mi, tar[j]);
        }
        int p = 0;
        for(int j = 0; j <= tot; j++) {
            if(mi + j < 0) fg[i][j] = 1;
            else fg[i][j] = 0; 
            int cnt = 0;
            while(p < tar.size() && -tar[p] <= j) {
                if(-tar[p] == j) cnt++;
                p++;
            }
            val[i][j] = cnt;
        }
    }
    ok[0] = 1;
    for(int i = 1; i < (1 << n); i++) {
        int vb = calbal(i);
        for(int j = 0; (1 << j) <= i; j++) {
            if(i & (1 << j)) {
                int bit = i ^ (1 << j);
                int cb = vb - bal[j].back();
                dp[i][1] = max(dp[i][1], dp[bit][1]);
                if(ok[bit] && cb >= 0) {
                    int flag = fg[j][cb];
                    dp[i][flag] = max(dp[i][flag], dp[bit][0] + val[j][cb]);
                    ok[i] |= !flag;
                }
            }
        }
    }
    int ans = dp[(1 << n) - 1][1];
    if(ok[(1 << n) - 1]) ans = max(ans, dp[(1 << n) - 1][0]);
    cout << ans << endl;
}
 
posted @ 2021-10-13 23:24  limil  阅读(80)  评论(0编辑  收藏  举报