40%:
暴力枚舉每一個 ? 是變成左括號還是右括號,再檢查合法性,取費用最小的合法方案。
時間複雜度:
(反思:但我在考試的時候居然打錯了暴力,多組數據忘記清空,只得了 20 分)
60%:
如果做出了 1989“圓括號”一題,再結合上面的暴力,不難想到 DP 的方法。
記
注意轉移的過程中要保證任意時刻左括號個數都必須多於或等於右括號個數。
對於已給定的括號,可以直接跳過(
時間複雜度:
100%:
要保證滿足條件,決策位置和合法性都不能不考慮,上面的 DP 已是最優可能,但無法解決 105 這麼大規模。因此正解並不是對上面的 DP 再進行玄學優化。
考試的時候其實是有想到貪心的,但卻無從下手。正解就是貪心。
輸入的時候,先算出每個 ? 的
第一步,什麼都不考慮,把全部都變成右括號。當前費用爲
之後,從第 1 位開始,從左向右逐位考慮,將到目前爲止左括號個數與右括號個數的差值記爲
對於一個合法的括號序列,前
所謂調整,就是在
爲了最小化費用,顯然要選一個使
另外,類似於上面的 DP,對於已給定的括號,如果直接跳過,要注意判斷可能出現的在前面選一個變成左括號的時候沒得選的局面。如果是對
時間複雜度:
正確性證明:暫略。
參考代碼:
#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 1e5 + 100;
int c;
long long diff[MAXN];
int l;
char str[MAXN];
long long ans;
priority_queue <long long, vector<long long>, greater<long long> > pq;
void solve() {
int k = 0, s = 0;
while (!pq.empty()) pq.pop();
for (int i = 0; i < l; i++) {
if (str[i] == '(') ++s; else --s;
if (str[i] == '?') pq.push(diff[k++]); //將來可以變成左括號
if (s == -1) //不合法的情況,在前面選一個變成左括號
if (pq.empty()) { //沒得變的情況,不合法
ans = -1;
return;
} else {
ans += pq.top(); //否則選一個花費最小的變
pq.pop(); s = 1;
}
}
if (s) ans = -1; //到最後左括號比右括號多也不合法
}
int main(void) {
freopen("2088.in", "r", stdin);
freopen("2088.out", "w", stdout);
while (~scanf("%s", str)) {
c = 0; //一開始多組數據忘記清零,暴力都 WA 了
l = strlen(str);
for (int i = 0; i < l; i++) c += str[i] == '?';
ans = 0LL;
for (int i = 0; i < c; i++) {
long long A, B; scanf("%lld%lld", &A, &B);
diff[i] = A - B;
ans += B; //先全部變成右括號
}
solve();
printf("%lld\n", ans);
}
return 0;
}