zoukankan      html  css  js  c++  java
  • [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_isim r_i$和$l_jsim r_j$,且$l_j<r_i$。

    有一个很简单的解决方法,可以直接将这两个区间拆成$l_isim l_j-1,l_jsim r_i,r_i+1sim 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++

  • 相关阅读:
    线性代数思维导图——3.向量
    微分中值定理的基础题型总结
    构造函数
    Python课程笔记(七)
    0241. Different Ways to Add Parentheses (M)
    0014. Longest Common Prefix (E)
    0013. Roman to Integer (E)
    0011. Container With Most Water (M)
    0010. Regular Expression Matching (H)
    0012. Integer to Roman (M)
  • 原文地址:https://www.cnblogs.com/wzc521/p/11741706.html
Copyright © 2011-2022 走看看