CF3D题解
https://codeforces.com/problemset/problem/3/D
对于括号串的合法性等价于满足,将(变成1,)变成-1,任意前缀不为负数。
满足一个合法括号串有多种填法,一次调整是将两个不同的括号翻转,我们希望翻转在使结果更优的情况下合法,于是将左边的)和右边的(互换,这样在满足总代价越来越小的同时不影响其他括号。
容易证明最优解是能够被上述调整法到达的
具体做法:
step1 尽可能把)放前面,在满足合法性情况下,先构造一个自由度最高的填法,给到达最优解创造机会。
step2 访问到(,如果总代价更优,考虑和前面的)对调,并且选择最优的调换法,次优和次次优的)在后面依旧有机会对调
记前面为\(pos_i\),后面为\(pos_j\),代价为$ pri$变化为 \(-l[pos_i] + r[pos_i] -r[pos_j] +l[pos_j]\),利用优先队列维护和 \(pos_i\)相关的
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
#define ll long long
string s;
int n;
int p[N],pos[N];
int l[N], r[N];
int turn[N];
int now[N];
ll pri;
//构造初始方案,先全填( ,turn[i]数组计算将'(转换成)的最大数,能转则转
void pre(){
bool f = 0;
int tj = 0;
int w1 = 0, w2 = 0;
for (int i = 1; i < s.size();i++){
if(s[i]=='(')tj++,w1++,now[i]=1;
if(s[i]=='?')tj++;
if(s[i]==')')tj--,w2++,now[i]=2;
if(tj<0)f = 1;
turn[i] = tj / 2;
}
if(w1>(s.size())/2 ||w2>(s.size())/2)f = 1;
for (int i = s.size()-2; i >= 1;i--)
turn[i] = min(turn[i], turn[i + 1]);
int tt = 0;
tj = 0;
for (int i = 1; i < s.size();i++){
if(!now[i]){
if(tt<turn[i])//能转
now[i] = 2,w2++,tt++,pri+=r[i];
else
now[i] = 1, w1++,pri+=l[i];
}
}
if(w1!=w2)
f = 1;
if(f){cout << "-1";exit(0);}
}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> dl;
int main(){
cin >> s;
s = " " + s;
for (int i = 1; i < s.size();i++){
if(s[i]=='?')
p[i] = 1,n++,pos[n]=i;
}
for (int i = 1; i <= n;i++){
cin >> l[pos[i]] >> r[pos[i]];
}
pre();
// for (int i = 1; i <s.size();i++)
// cout << now[i];
//cout << pri << "\n";
for (int i = 1; i <s.size();i++){//核心算法
if(s[i]=='?'){
if(now[i]==2)
dl.push(make_pair(-r[i] + l[i], i));//可能右移的)
else{
if(dl.empty())
continue;
if(dl.top().first-l[i]+r[i]<0){
pri += dl.top().first - l[i] + r[i];
swap(now[dl.top().second], now[i]);
dl.pop();
dl.push(make_pair(-r[i] + l[i], i));
}//如果能调整,选择最优的
}
}
}
cout << pri << "\n";
for (int i = 1; i <s.size();i++){
if(s[i]=='?'){
if(now[i]==1)
cout << '(';
else
cout << ')';
}
else
cout << s[i];
}
return 0;
}
``

浙公网安备 33010602011771号