CF1248D1 The World Is Just a Programming Task (Easy Version)
洛谷链接
思路:
(貌似没有找到这题的强化版本  ̄□ ̄||)
看到题目感觉有点懵,再看一眼数据范围,$n \le 500$,那自然是暴力枚举了。
时间复杂度的上限是 $O(N^3)$,枚举两个交换的位置是 $O(N^2)$,难点是 $O(N)$ 算出每种字符串对应的答案。
到这里我就已经不会了,剩下的就是看题解了。
很显然,当字符串 $S$ 中左括号数量不等于右括号数量,那么它一定不合法。
转换一下判定合法序列的条件:设 $cnt_i(i \in [1,n])$ 为 $S_{1 \cdots i}$ 中,左括号的数量减去右括号的数量。
那么不难发现,如果 $\text{S}$ 是一个合法的字符串,必然满足 $\forall i \in [1,n],cnt_i \ge 0$。
然后,把一段极长的非合法前缀找出来,放到后面,就一定能够形成一个合法字符串。
即找出第一个 $cnt_i$ 的值最小的 $i$ 即可。
根据前缀和的性质,它移到后面,那么 $[i + 1,n]$ 这一段的 $cnt$ 值都要减去 $cnt_i$。
又因为 $cnt_i$ 最小,故已经满足了 $cnt_k \ge 0(k \in [i + 1,n])$。
而原先的 $[1,i]$ 这一段,因为整个字符串里左括号数量等于右括号数量,
故它缺的那一部分左括号,原先的 $[i+1,n]$ 这一段可以给它补上。这样整个字符串就合法了。
还有一种情况,就是存在 $cnt_j = cnt_i$ 且 $j \gt i$ 的情况(这里的 $i$ 指的是上述 $cnt_i$ 取得最小的那个)。
也就是说 $S_{i+1 \cdots j}$ 这一段是合法的,不难发现,把 $S_{1 \cdots j}$ 移到后面,同样合法。
所以最终的算法就是:统计 $cnt$ 的最小值 $mincnt$,再统计使得 $cnt_i = mincnt$ 的 $i$ 的数量即可。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 505; 6 char s[maxn]; 7 int n; 8 int calc() { 9 int cnt = 0,minnum = 0; 10 for(int i = 1;i <= n;++ i) { 11 cnt += s[i] == '(' ? 1 : -1; 12 minnum = min(minnum , cnt); 13 } 14 int ans = 0; 15 for(int i = 1;i <= n;++ i) { 16 cnt += s[i] == '(' ? 1 : -1; 17 ans += cnt == minnum; 18 } 19 return ans; 20 } 21 int main() { 22 scanf("%d%s",&n,s + 1); 23 int a = 0,b = 0; 24 for(int i = 1;i <= n;++ i)a += s[i] == '(',b += s[i] == ')'; 25 if(a != b) { 26 puts("0\n1 1"); 27 return 0; 28 } 29 int ans = 0,t,l = 1,r = 1; 30 for(int i = 1;i <= n;++ i) { 31 for(int j = 1;j <= n;++ j) { 32 swap(s[i] , s[j]); 33 if((t = calc()) > ans)ans = t,l = i,r = j; 34 swap(s[i] , s[j]); 35 } 36 } 37 printf("%d\n%d %d",ans,l,r); 38 return 0; 39 }QAQ