0718题解-FOIWC实在是太美
Posted dqk2003
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
以上是关于0718题解-FOIWC实在是太美的主要内容,如果未能解决你的问题,请参考以下文章