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++

  • 相关阅读:
    文件编程---库函数
    linux文件编程----系统调用
    makefile工程管理
    快速上手日期插件laydate
    js判断浏览器类型以及语言
    谷歌支付服务端详细讲解(PHP)
    php中的date和strtotime函数妙用
    本地搭建GitLab
    mysql查询语句常用字段操作函数
    php中签名公钥、私钥(SHA1withRSA签名)以及AES(AES/ECB/PKCS5Padding)加密解密详解
  • 原文地址:https://www.cnblogs.com/wzc521/p/11741706.html
Copyright © 2011-2022 走看看