cf1598f RBS

定义括号序列为只包括 \(\texttt{(}\)\(\texttt{)}\) 的字符串。一个匹配的括号序列 (简记为 RBS) 满足,可以在其中加入 \(1\)\(+\),将其转化为合法的代数式,例如:

  • \(\texttt{()()}\)\(\texttt{(())}\) 是匹配的
  • \(\texttt{)(}\)\(\texttt{(}\)\(\texttt{)}\) 不是

将两个字符串拼接在一起简记为 \(x+y\) 。例如,\(\texttt{()()}+\texttt{)(}=\texttt{()())(}\) .

给定 \(n\) 个括号序列 \(s_1\sim s_n\) ,你可以将他们任意重新排序,要求使得最终排序后的字符串满足 \(s_1+\dots+s_n\) 的 RBS 前缀个数最多。

\(1\leq n\leq 20,\sum |s_i|\leq 4\cdot 10^5\)

\(20=\) 状压 .

\(dp(S)\) 表示已经选择集合为 \(S\) 的字符串的最大代价.

首先,如果 \(S\) 中的字符串排列出现 \(<0\) 的位置肯定不合法. 但是,观察得到,对于状态 \(S\)\(\texttt{(}\) 的个数 \(-\) \(\texttt{)}\) 的个数的值是一定的,所以,对于新加入字符串能产生贡献的位置也是固定的. 考虑加入 \(s_i\) ,设加入 \(s_i\) 的贡献是 \(w\) ,那么出现两种情况,加入 \(s_i\) 之后使得整个字符串出现 \(<0\) 的位置,那么,\(dp(S)+w\) 与答案取最大值即可;否则,用 \(dp(S)+w\) 去跟新 \(dp(s+i)\) .

现在剩下的问题就是如果求出对于 \(s_i\) 中每种位置的贡献,产生贡献的位置必定是前缀最小值,并且一定是非正数,所以考虑先处理出这些位置,发现单调不增,那么相同的一段的数量就是当前此种位置的贡献了.

(本来是看到 ds 的 tag 才去做的,ds 呢,用到 ds 了吗?)

时间复杂度 : \(O(\sum|s_i|+2^n)\)

空间复杂度 : \(O(2^n+\sum |s_i|)\)

code

#include<bits/stdc++.h>
using namespace std;
const int N=20,L=4e5+10;
int n;
string str[N];
int w[N][L],mn[N];
int a[N],sum[1<<N];
int dp[1<<N];
int ans=0;
void work(string s,int id){
	vector<int>v;
	int tmp=0;
	for(int i=0;i<(int)s.size();i++){
		tmp+=s[i]==')'?-1:1;
		mn[id]=min(mn[id],tmp);
		if(s[i]==')'&&tmp<=0&&tmp==mn[id])v.push_back(-tmp);
	}
	a[id]=tmp;
	for(int i=0;i<(int)v.size();i++){
		int cnt=0,j=i;
		for(j=i;j<(int)v.size();j++){
			if(v[i]==v[j])++cnt;
			else break;
		}
		w[id][v[i]]=cnt;
		i=j-1;
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=0;i<n;i++)cin>>str[i];
	memset(mn,0x3f3f3f3f,sizeof(mn));
	for(int i=0;i<n;i++)work(str[i],i);
	for(int mask=0;mask<(1<<n);mask++){
		for(int i=0;i<n;i++)if(mask&(1<<i))sum[mask]+=a[i];
	}
	memset(dp,-0x3f3f3f,sizeof(dp));
	for(int i=0;i<n;i++){
		if(mn[i]<0)ans=max(ans,w[i][0]);
		else{
			dp[1<<i]=w[i][0];
			ans=max(ans,dp[1<<i]);
		}
	}
	for(int mask=1;mask<(1<<n);mask++){
		if(dp[mask]<0)continue;
		for(int i=0;i<n;i++){
			if((mask&(1<<i))||sum[mask]<0)continue;
			if(sum[mask]+mn[i]<0)ans=max(ans,dp[mask]+w[i][sum[mask]]);
			else{
				dp[mask|(1<<i)]=max(dp[mask|(1<<i)],dp[mask]+w[i][sum[mask]]);
				ans=max(ans,dp[mask|(1<<i)]);
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}
/*inline? ll or int? size? min max?*/

posted @ 2022-01-07 21:56  xyangh  阅读(15)  评论(0)    收藏  举报