0718题解-FOIWC实在是太美
作者:互联网
题目描述
艾斯洛克希望你送给她一个长度为\(N\)的合法括号序列,保证\(N\)是偶数。你在序列的第\(i\)位放左括号的代价为\(A_i\) ,放右括号的代价为\(B_i\)。艾斯洛克不想让你太麻烦,所以希望你支付最小的代价。不过才不是担心你呢!真的不是哦!虽然众所周知,但艾斯洛克还是给了你合法括号序列的定义,以防你这个八嘎会错意了:
-
空序列是合法括号序列;
-
如果\(A\)是合法括号序列,那么\((A)\)是合法括号序列;
-
如果\(A,B\)都是合法括号序列,那么\(AB\)是合法括号序列。
"知......知道了的话就快去准备吧!虽然我也不是很期待就是了!!”艾斯洛克说。
思路&题解
\(n^2\)的dp很简单,主要是思考怎么贪心,我在考场上已经想到了带反悔贪心,并且用堆实现,但是没有想到正确的贪心决策方法。
我自己在考场上想到是先贪心选择小的代价,如果左括号不够的话再反悔前面的价值大的右括号,最后如果左括号多了的话,再反悔处理
最后反悔我是找最右边的反悔,然后删去左侧最大反悔代价的括号。但是是错的,可以举出反例,比如最后一个匹配了最大的,次大的在最大的右边,
且中间还有一些比较小代价的括号。我们可以把当前最右侧的和次大的匹配,然后后面的匹配最大的,否则的话,次大的可能要反悔但是不优。
其实这里贪心没有想到一个重要的性质
一个合法的括号序列,如果一个右括号变成左括号,一定可以在它的后边(包括自己)把一个左括号再变回右括号形成一个新的合法括号序列。可以考虑折线图来证明
我们考虑增量法来贪心,假设当前构造好了长度为i的且合法的最小代价的括号序列,我们增加两个位置怎么办。
由于要求合法,所以最后一个是右括号,而倒数第二个可以是右括号,也可以是左括号。右括号直接就是合法的,左括号的话要从前面再找一个右括号进行反悔,肯定是找最小的代价反悔
所以我们把右括号的A-B放入小根堆,然后贪心的选择即可。(看来增量法应该是和反悔贪心有联系的)
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 3e5 + 11;
int n;
LL A[N], B[N];
priority_queue<LL, vector<LL>, greater<LL> > q;
int main(){
freopen("beautiful.in", "r", stdin);
freopen("beautiful.out", "w", stdout);
cin>>n;
for(int i = 1;i <= n; i++){
scanf("%lld", &A[i]);
}
for(int i = 1;i <= n; i++){
scanf("%lld", &B[i]);
}
LL ans = A[1] + B[2];
q.push(A[2] - B[2]);
for(int i = 3;i <= n; i += 2){
LL cur = q.top();
if(cur + B[i] + B[i+1] <= A[i] + B[i+1]){
ans += cur + B[i] + B[i+1];
q.pop(); q.push(A[i] - B[i]); q.push(A[i+1] - B[i+1]);
}
else{
ans += A[i] + B[i+1];
q.push(A[i+1] - B[i+1]);
}
}
cout<<ans<<endl;
return 0;
}
标签:FOIWC,括号,题解,合法,0718,反悔,序列,代价,贪心 来源: https://www.cnblogs.com/dqk2003/p/13338666.html