topcoder srm 688 div1 -3

1、给出一个只包含'(',')'的字符串$s$,现在对它进行若干次如下操作使其变成匹配的括号串(每次操作包含3个步骤):(1)选择 $L,R,L\leq R$;(2)将$L,R$之间的字符翻转;(3)将$L,R$之间所有的'('改成')',所有的')' 改成'('。

思路:首先去掉原串中已经配对的括号,那么剩下的括号序列是以下情况:(1)空串;(2)))));(3)((((;(4)))))((。第一种情况不需要处理。对于第三种情况和第四种情况,都可以将其变成第二种情况。第二种情况只需要对前一半做操作即可。另外,每次操作的时候不会影响已经删掉的匹配的串。

#include <stdio.h>
#include <string>
#include <stack>
#include <vector>
#include <string.h>
#include <algorithm>
#include <assert.h>
#include <iostream>
using namespace std;


const int N=100005;



void deal(string &s,int L,int R)
{
    reverse(s.begin()+L,s.begin()+R+1);
    for(int i=L;i<=R;++i)
    {
        if(s[i]=='(') s[i]=')';
        else s[i]='(';
    }
}

vector<int> cal(string S)
{
    deque<int> st;
    for(int i=0;i<(int)S.size();++i)
    {
        if(S[i]=='(') st.push_back(i);
        else if(!st.empty()&&S[st.back()]=='(') st.pop_back();
        else st.push_back(i);
    }
    if(st.empty()) return vector<int>{};
    vector<int> ans;

    for(int i=0;i<(int)st.size();++i)
    {
        if(S[st[i]]=='(')
        {
            int L=st[i];
            int R=st.back();
            ans.push_back(L);
            ans.push_back(R);
            deal(S,L,R);
            vector<int> tmp=cal(S);
            for(int i=0;i<(int)tmp.size();++i) ans.push_back(tmp[i]);
            return ans;
        }
    }
    ans.push_back(st.front());
    ans.push_back(*(st.begin()+st.size()/2-1));
    return ans;
}

class ParenthesesDiv1Easy
{
public:
    vector<int> correct(string s)
    {
        if((s.size())&1) return vector<int>{-1};
        return cal(s);
    }
};

2、给出一个只包含'(',')'的字符串$s$。对它做一些操作之后使得$L_{i},R_{i}$之间的串是匹配的括号串。每次操作选择两个位置$i,j$然后交换这两个位置的字符。给出最少的操作数。

思路:

(1)首先,如果$[x,y_{1}],[x,y_{2}],y_{1}< y_{2}$都要是匹配的,那么有$[x,y_{1}],[y_{1}+1,y_{2}]$之间要分别匹配。

(2)其次,如果$[x_{1},y_{1}],[x_{2},y_{2}]$都要匹配,$x_{1}<x_{2}<y_{1}<y_{2}$,那么$[x_{1},x_{2}-1],[x_{2},y_{1}],[y_{1}+1,y_{2}]都要匹配$

经过这样的处理,最后所有匹配的区间可能会有包含的关系,然后没有其他相交关系。所以可以按照区间长度排序,一个区间一个区间进行处理。每个区间都相等于一个小的串(对于包含小区间的大区间不需要考虑已经处理的小区间,只需要考虑除了小区间外其他的部分匹配)。对于一个串$T$,定义$f(T)$表示将$T$变成匹配最少的修改数,这个修改指的是单独把左括号改成右括号或右括号改成左括号的次数,而不是交换。最后的答案其实是重复了一半,除以2即可。还有一个问题是有可能所有考虑的范围内的左括号数过于多,以至于在未考虑区间根本没有足够的右括号可以交换,这种情况是无解的。

#include <stdio.h>
#include <string.h>
#include <string>
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
using namespace std;

const int N=2005;

int n,m,f[N][N];
int a[N],b[N];


int cmp(pair<int,int> a,pair<int,int> b)
{
    int ll=a.second-a.first;
    int rr=b.second-b.first;
    if(ll!=rr) return ll<rr;
    return a.first<b.first;
}


int tot0,tot1,totlen;

void up(int &x,int y)
{
    if(x==-1||x>y) x=y;
}


int cal(string s)
{

    if(s.size()&1) return -1;

    const int L=1;
    const int R=(int)s.size();

    for(int i=L-1;i<=R;++i) memset(f[i],-1,sizeof(f[i]));
    f[L-1][0]=0;
    for(int i=L;i<=R;++i)
    {
        if(s[i-1]=='(') ++tot0;
        else ++tot1;
        int w;
        if(s[i-1]==')') w=1; else w=0;
        for(int j=1;j<=n;++j) if(f[i-1][j-1]!=-1) up(f[i][j],f[i-1][j-1]+w);
        if(s[i-1]==')') w=0; else w=1;
        for(int j=0;j<n;++j) if(f[i-1][j+1]!=-1) up(f[i][j],f[i-1][j+1]+w);
    }
    totlen+=(R-L+1)>>1;
    return f[R][0];
}



vector<int> RR[N];
vector<pair<int,int> > req;


class ParenthesesDiv1Medium
{
public:
	int minSwaps(string s,vector<int> L,vector<int> R)
	{
	    n=(int)s.size();
	    m=(int)L.size();
	    for(int i=1;i<=n;++i)
        {
            if(s[i-1]=='(') a[i]=0;
            else a[i]=1;
        }
        for(int i=0;i<m;++i) ++L[i],++R[i];
        for(int i=0;i<m;++i) ++b[L[i]],--b[R[i]+1];
        for(int i=1;i<=n;++i) b[i]+=b[i-1];

        for(int i=0;i<m;++i) RR[L[i]].push_back(R[i]);
        for(int i=1;i<=n;++i) if(RR[i].size())
        {
			sort(RR[i].begin(),RR[i].end());
			RR[i].erase(unique(RR[i].begin(),RR[i].end()),RR[i].end());

			for(int j=1;j<(int)RR[i].size();++j) RR[RR[i][j-1]+1].push_back(RR[i][j]);
			RR[i].resize(1);
			int x=RR[i][0];
			for(int j=n;j>i;--j) if(RR[j].size()&&RR[j].back()>=RR[i][0]) x=min(x,j-1);
			if(x<RR[i][0])
            {
				RR[x+1].push_back(RR[i][0]);
				RR[i][0]=x;
			}
			req.push_back({i,RR[i][0]});
		}

        int ans=0;
        sort(req.begin(),req.end(),cmp);
        for(int i=0;i<(int)req.size();++i)
        {
            int ll=req[i].first;
            int rr=req[i].second;
            string ss="";
            for(int j=ll;j<=rr;++j) if(s[j-1]!='*') ss+=s[j-1],s[j-1]='*';
            int tmp=cal(ss);
            if(tmp==-1) return -1;
            ans+=tmp;
        }
        if(tot0>totlen) ans+=tot0-totlen;
        else ans+=abs(totlen-tot1);
        for(int i=1;i<=n;++i) if(!b[i])
        {
            if(a[i]) --tot0;
            else     --tot1;
        }
        if(tot0>totlen||tot1>totlen) return -1;
        return ans>>1;
	}
};

  

转载于:https://www.cnblogs.com/jianglangcaijin/p/6919780.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值