[CSP-S模拟测试]:括号密码(贪心)
题目描述
在“无限神机”的核心上,有一个奇怪的括号密码,密码初始已经有一个括号序列,有$n$个限制条件,每个限制条件描述为$l_i$和$r_i$,表示区间$[l_i,r_i]$的括号序列必须合法。调整密码只有一种方式:交换括号序列的任意两个字符。求使得密码满足所有条件最少的交换次数。
括号序列合法定义为:
$1.$空串$""$是一个合法的括号序列
$2.$如果$"X"$和$"Y"$是合法的括号序列,则$"XY"$(连接$X$和$Y$)是合法的括号序列
$3.$如果$"X"$是合法的括号序列,则$"(X)"$是合法的括号序列
$4.$每个合法的括号序列均可由上述规则推导出。
例如,$"","()","()()()","(()())"$和$"(((())))"$都是合法的括号序列。显然,只有长度为偶数的才有可能是合法的括号序列
输入格式
第一行有一个字符串$s$,表示原括号序列
第二行一个整数$n$,表示限制条件的个数
第三行$n$个整数,表示$l_i$
第四行$n$个整数,表示$r_i$
输出格式
输出一个整数,表示最少交换次数,无解输出$-1$
样例
样例输入1:
)(
4
0 0 0 0
1 1 1 1
样例输出1:
1
样例输入2:
(((())
2
0 2
1 3
样例输出2:
2
数据范围与提示
样例$1$解释:
$L$和$R$描述了$4$个相同的条件,即要求从$s[0]$到$s[1]$是正确的括号序列。只需要做$1$次交换$s[0]$和$s[1]$就可以得到正确的括号序列。
样例$2$解释:
需要交换$(s[1],s[4])$和交换$(s[3],s[5])$
数据范围:
每个测试点的数据规模及特点如下表所示。设字符串长度为$|S|$
题解
先来考虑区间不重叠的情况:
对每个区间,统计区间前缀和,设前缀和最小值为$w$,区间总和为$a$($a$必须为偶数,否则无解) 使$a$变为$0$,需要从区间外引进括号,只计算引进’)‘数量,最后判断如果所有条件区间的’)‘不够,再从外面进口’)’ 若$a>0$,则需引进$\frac{a}{2}$个’)’,使得$a$变为$0$,每次贪心选择最右边的’(‘修改,$w$不改变(将这些进口操作记录到答案) 若$a<0$,需将这里的’)’出口到其它区间,贪心选择最左边的’)’修改,$w$会增加$|a|$(但这些修改不计算入答案,我们只计算引入’(‘数量) 最后加上$\frac{w}{2}$次区间内部操作,使得前缀和不存在负数。
再来考虑会出现重叠,但是无嵌套:
不妨设这两个区间分别为$l_i\sim r_i$和$l_j\sim r_j$,且$l_j<r_i$。
有一个很简单的解决方法,可以直接将这两个区间拆成$l_i\sim l_j-1,l_j\sim r_i,r_i+1\sim r_j$即可。
如果区间有嵌套仍可以按这种方法解决,代码实现细节比较多。
时间复杂度:$\Theta(n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec{int l,r;}e[100001];
int n;
int cnt=1,suml,sumr,resl,resr;
char ch[100001];
int l[100001],r[100001];
vector<int> vec[100001];
vector<char> s[100001];
map<vector<int>,int>mp;
int ans;
void work(int x)
{
ans+=(min(l[x],r[x])+1)>>1;
int mid=(l[x]-r[x])>>1;
if(mid<0)
{
mid=-mid;
int flag=min(mid,resl);
mid-=flag;
resl-=flag;
ans+=flag;
resr+=mid;
}
else
{
int flag=min(mid,resr);
mid-=flag;
resr-=flag;
ans+=flag;
resl+=mid;
}
}
int main()
{
scanf("%s%d",ch+1,&n);
int len=strlen(ch+1);
for(int i=1;i<=n;i++){scanf("%d",&e[i].l);e[i].l++;}
for(int i=1;i<=n;i++)
{
scanf("%d",&e[i].r);
e[i].r++;
for(int j=e[i].l;j<=e[i].r;j++)vec[j].push_back(i);
}
mp[vec[0]]=1;
for(int i=1;i<=len;i++)
{
if(!mp[vec[i]])mp[vec[i]]=++cnt;
s[mp[vec[i]]].push_back(ch[i]);
}
for(int i=0;i<s[1].size();i++)
if(s[1][i]=='(')suml++;
else sumr++;
for(int i=2,L,R;i<=cnt;i++)
{
L=R=0;
for(int j=0;j<s[i].size();j++)
{
if(s[i][j]=='(')L++;
else R++;
l[i]=max(l[i],R-L);
}
L=R=0;
for(int j=s[i].size()-1;j>=0;j--)
{
if(s[i][j]=='(')L++;
else R++;
r[i]=max(r[i],L-R);
}
if(abs(L-R)&1){puts("-1");return 0;}
}
for(int i=2;i<=cnt;i++)work(i);
int flag=min(resl,resr);
ans+=flag;
resl-=flag;
resr-=flag;
if(suml<resl||sumr<resr){puts("-1");return 0;}
ans+=resl+resr;
printf("%d",ans);
return 0;
}
rp++