智乃的括号匹配

智乃的括号匹配

题目描述

智乃现在有一个仅由 "(" 和 ")" 组成长度大小为 N 的字符串,字符串的每一个位置的字符都有自己的权值 vi ​和代价 ci​。

智乃定义一对在该字符串中配对的括号对,假设左括号的坐标为 i,右括号为 j,该括号对的价值为两个括号权值的乘积 vivj​。

"配对的括号"指从右往左开始对于每一个 '(',从它开始向右找到第一个未配对的 ')' 组成一对配对的括号。例如在 "(())" 中,假设下标分别为 1,2,3,4 首先找到 2 位置的 '(' 与 3 的 ')' 进行配对,接下来 1 位置的 '(' 由于 3 已经配对,所以最终 14 配对。

智乃定义整个字符串的价值为所有配对的括号对价值之和,注意并不要求整个字符串都是配对的。

现在对于任意下标为 i 的括号,智乃可以消耗它的代价 ci ​将其翻转,即 "(" 变为 ")",")" 变为 "("。

假设智乃的最总得分为经过若干次操作后,字符串的价值减去操作的代价之和,求智乃的最大得分。

注意当字符串价值小于操作的代价和时,智乃的得分可能是负数。

输入描述:

第一行输入一个正整数 N(1N500) 表示字符串的大小。

接下来输入一个长度大小为 N 的字符串 S(Si{(,)})

接下来输入 N 个正整数 vi(106vi106) 表示字符串每个字符的权值。

接下来输入 N 个正整数 ci(1ci109) 表示字符串每个字符的代价。

输出描述:

仅一行一个整数,表示最大得分。

示例1

输入

2
)(
4 3
10 10

输出

0

说明

如果将两个括号都翻转,则代价为 20,收益为 12,这样倒亏 8,不如什么都不做。

示例2

输入

2
((
4 3
10 10

输出

2

说明

翻转第二个括号,则代价为 10,收益为 12,最大得分为 2。

示例3

输入

4
()()
-1 100 10 10
1000 101 100 100

输出

789

说明

最优操作位变成 "(())"。得分 990,代价为 201。

示例4

输入

2
()
-1 100
10 10

输出

-10

说明

如果放着不动,会直接亏 100,不如翻转一下主动亏 10

 

解题思路

  官方题解写的什么鬼,完全看不懂。

  注意到当把所有成对匹配的括号删去,那么剩余元素必然是 )...))((...( 的形式,即左部分全是 )(或空集),右部分全是 ((或空集)。把分界点记作 k,显然原序列的 s1sk 中的 ( 只会与 s1sk 中的 ) 匹配。同理原序列的 sk+1sn 中的 ) 只会与 sk+1sn 中的 ( 匹配。所以当分界点 k 确定后左右两部分是独立的。

  为此定义 li 表示将 s1si 中的 ( 进行匹配使剩余元素均为 )(或空集)所能获得的最大得分,定义 ri 表示将 sisn 中的 ) 进行匹配使剩余元素均为 ((或空集)所能获得的最大分数。

  考虑如何通过 li1 转移得到 li。如果 si 不会被匹配,那么就需要把 s1si1 的 ( 匹配掉,再让 si 变成 ),转移方程就有 li=li1+wi,1。这里的 wi,0/1 表示将 si 变成 ( 或 ) 的代价(0ci)。否则如果 sisj (1j<i) 匹配,那么就需要把 sj+1si1 的括号都匹配完(显然 ij+1 应该为偶数),再让 sj( si 变 )。先定义 f(i,j) 表示将 sisj(长度为偶数)都匹配完所能获得的最大分数。因此转移方程就是 li=max1j<iij+1 is even{lj1+f(j+1,i1)+wj,0+wi,1+vjvi}

  综上有 li=max{li1+wi,1,max1j<iij+1 is even{lj1+f(j+1,i1)+wj,0+wi,1+vjvi}}

  同理考虑从 ri+1 转移到 ri,转移方程为 ri=max{ri+1+wi,0,maxi<jnji+1 is even{rj+1+f(i+1,j1)+wi,0+wj,1+vivj}}

  而 f(i,j) 就是一个区间 dp。如果 sisj 匹配,那么转移方程就是 f(i,j)=f(i+1,j1)+wi,0+wj,1+vivj。否则由于 sisj 都要匹配完,枚举 k,分别将 sisksk+1sj 匹配完,转移方程就是 f(i,j)=maxi<k<j{f(i,k)+f(k+1,j)}

  最后答案就是 max0in{li+ri+1}

  AC 代码如下,时间复杂度为 O(n3)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 505;

char s[N];
int v[N], c[N], w[N][2];
LL f[N][N], l[N], r[N];

int main() {
    int n;
    scanf("%d %s", &n, s + 1);
    for (int i = 1; i <= n; i++) {
        scanf("%d", v + i);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", c + i);
        if (s[i] == '(') w[i][1] = -c[i];
        else w[i][0] = -c[i];
    }
    for (int len = 2; len <= n; len += 2) {
        for (int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            f[i][j] = f[i + 1][j - 1] + w[i][0] + w[j][1] + 1ll * v[i] * v[j];
            for (int k = i + 1; k + 1 < j; k += 2) {
                f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j]);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        l[i] = l[i - 1] + w[i][1];
        for (int j = i - 1; j >= 1; j -= 2) {
            l[i] = max(l[i], l[j - 1] + f[j + 1][i - 1] + w[j][0] + w[i][1] + 1ll * v[j] * v[i]);
        }
    }
    for (int i = n; i; i--) {
        r[i] = r[i + 1] + w[i][0];
        for (int j = i + 1; j <= n; j += 2) {
            r[i] = max(r[i], r[j + 1] + f[i + 1][j - 1] + w[i][0] + w[j][1] + 1ll * v[i] * v[j]);
        }
    }
    LL ret = -1e18;
    for (int i = 0; i <= n; i++) {
        ret = max(ret, l[i] + r[i + 1]);
    }
    printf("%lld", ret);
    
    return 0;
}

 

参考资料

  【题解】牛客练习赛124_PLUS:https://ac.nowcoder.com/discuss/1298659

posted @   onlyblues  阅读(50)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2023-04-27 闇の連鎖
Web Analytics
点击右上角即可分享
微信分享提示