1 条题解

  • 0
    @ 2025-5-6 20:31:51

    一道结合背包的思想(dp)、dp通过两维的和为来定值来将O(n2)O(n ^ 2)降到O(2n)O(2n)的优化、括号匹配的思想(栈)、前缀和的思想(转化成-1和+1)的好题。

    首先根据题目给出的定义,我们可以考虑进行一个转化,把左括号看成 +1+1,右括号看成 1-1,这样合法括号序列可以看成任意前缀和非负、区间和为 00 的序列。

    我们由此可以列出一个 O(n3)O(n^3) 的动态规划方案:dpi,j,kdp_{i,j,k}表示染色到第 ii 项,标记G的前缀和为 jj,标记H的前缀和为 kk 的方案。

    转移如下:$dp_{i,j,k} = dp_{i-1,j - A_i,k} + dp_{i-1,j,k - A_i}$,初始化dp0,0,0=1dp_{0,0,0} = 1,其中 AiA_i 表示经过转换之后我们的数组的第 ii 项。

    但是这个方法时间复杂度很高,我们考虑进一步优化。

    我们发现对于一个 ii 而言,有用的 j+kj+k 是不变的,即数组 AA 的前 ii 项的前缀和,这点观察式子也可以发现。

    所以我们只维护 jj 一维,保证 kk 不为负数即可。

    dpi,j=dpi1,jAi+dpi1,jdp_{i,j} = dp_{i-1,j - A_i} + dp_{i-1,j}

    kk非负如何保证?我们发现k=sumjk = \text{sum} - j,其中sumsum 是当前前ii项的前缀和,我们只要保证jsumj \le \text{sum}即可。

    时间复杂度:O(n2)O(n^2)

    #include <iostream>
    #include <vector>
    #define endl '\n'
    using namespace std;
    using LL = long long;
    const int mod = 998244353;
    
    int main(){
    	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    	string s;
    	cin >> s;
    	int n = s.size();
    	s = " " + s;
    	vector<int> a(n + 1);
    	vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0));
    	for(int i = 1; i <= n; i++){
    		if(s[i] == '(') a[i] = 1;
    		else a[i] = -1;
    	}
    	dp[0][0] = 1;
    	LL sum = 0;
    	for(int i = 1; i <= n; i++){
    		sum += a[i];
    		for(int j = 0; j <= sum; j++){
    			dp[i][j] = dp[i - 1][j];
    			if(j - a[i] >= 0) dp[i][j] = (dp[i][j] + dp[i - 1][j - a[i]]) % mod;
    		}
    	}
    	cout << dp[n][0] << endl;
    	return 0;
    }
    
    • 1

    信息

    ID
    2281
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    7
    已通过
    1
    上传者