http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4027
dp题,比赛的时候的知道是dp,但是就是想不出来,回来虽然想出来了,但是调了2个多小时才调出来。
好像同队的大佬也有用线段树写的,0rz。
注意理解下,每个左括号都可以换到他右边的所有右括号,但是前提是这个右括号要先和他们之间的所有左括号先换一下,这个左括号要和他们之间所有的右括号也换一下。
然后我对于每个右括号 j 设一个dp[ j ], 用来存这里的能换到的最大值。
思路就是从左往右对于每个左括号 i 往右遍历,遇见左括号就加上(计算前缀和,这里叫做tem_sum),然后遇见右括号 j 就把前缀和和他的值乘一下,然后这样还不够因为还要考虑左括号们移过来和前面的右括号产生的值(这里叫做tem_val),然后用 tem_sum*val[ j ] + tem_val 尝试更新一下dp[ j ]。
然后考虑下tem_val的获取,对于每个左括号i,他的起始tem_val应该是他左边区间dp所能拿到的最大值,当然也可以不拿,就取0。然后对于他右边的右括号 j 每次更新一个 dp[j] 之后,tem_val更新成这个 dp[ j ] 就可以了,因为 dp[ j ] 已经是 i 和 i 左边所有其他左括号移到这里的情况下可以拿到的最大值了。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 5;
int figure[MAXN], val[MAXN];
long long dp[MAXN];
int main(){
int t, n;
int i, j;
long long tem_sum, tem_val;
char tc;
scanf("%d", &t);
while(t--){
memset(dp, 0, sizeof(dp));
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
scanf("%c", &tc);
switch(tc){
case '(':
figure[i] = 0;
break;
case ')' :
figure[i] = 1;
break;
default:
i--;
}
}
for (i = 1; i <= n; i++){
scanf("%d", &val[i]);
}
for (i = 1; i <= n; i++){
if(figure[i]){
dp[i] = max(dp[i], dp[i - 1]);
}
else{
tem_sum = val[i];
tem_val = dp[i - 1]<0?0:dp[i - 1];
dp[i] = tem_val;
for (j = i + 1; j <= n; j++)
{
if(figure[j]){
if(figure[j] == 1){
figure[j] = 2;
dp[j] = tem_sum * val[j] + tem_val;
}
else{
dp[j] = max(dp[j], tem_sum * val[j] + tem_val);
}
tem_val = dp[j];
}
else{
tem_sum += val[j];
}
}
}
}
printf("%lld\n", dp[n]);
}
return 0;
}