Description
给出nn个二元组,每次可以选择两个相邻的二元组<sk,vk>,<sk+1,vk+1><sk,vk>,<sk+1,vk+1>交换当且仅当sk=′(′,sk+1=′)′sk=′(′,sk+1=′)′,该次交换的价值为vk⋅vk+1vk⋅vk+1,可以进行任意次数的交换,问价值和最大值
Input
第一行一整数TT表示用例组数,每组用例首先输入一整数表示二元组个数,之后输入一个字符串ss表示这个二元组的第一维,之后输入nn个整数表示这nn的二元组的第二维
Output
输出最大价值和
Sample Input
4
6
)())()
1 3 5 -1 3 2
6
)())()
1 3 5 -100 3 2
3
())
1 -1 -1
3
())
-1 -1 -1
Sample Output
24
21
0
2
Solution
第ii个左括号如果到第个位置,那么后面的左括号必然都在第jj个位置后面的位置,进而如果第个左括号移动到第jj个位置或之后可以分成两种情况:第个左括号移动到第jj个位置且第个左括号移动到第j+1j+1个位置或之后,第ii个左括号移动到第个位置或之后,以此转移
统计左括号的数量nlnl和每个左括号在序列中的位置pos[1],...,pos[nl]pos[1],...,pos[nl],第ii个左括号前面的右括号数量以及前ii个右括号权值和以便统计第ii个左括号右移时的贡献
以表示第ii个左括号移到第个位置或之后,第ii个括号到第个括号对答案的贡献最大值
由于后nl−inl−i个左括号必然在jj位置之后,所以的范围为pos[i]≤j≤n−(nl−i)pos[i]≤j≤n−(nl−i),第ii个左括号到达第个位置所经过的右括号是从第num[i]+1num[i]+1个右括号开始,经过j−pos[i]j−pos[i]个右括号,所以其贡献val(i,j)=vpos[i]⋅(sum[num[i]+j−pos[i]]−sum[num[i]])val(i,j)=vpos[i]⋅(sum[num[i]+j−pos[i]]−sum[num[i]]),进而有转移
dp[i][j]=max(dp[i][j+1],val(i,j)+dp[i+1][j+1])dp[i][j]=max(dp[i][j+1],val(i,j)+dp[i+1][j+1])
最后max(dp[1][j])max(dp[1][j])即为答案 ,时间复杂度O(n2)O(n2)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int T,n,nl,nr,val[maxn],num[maxn],sum[maxn],pos[maxn];
ll dp[maxn][maxn];
char s[maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
nl=nr=0;
for(int i=1;i<=n;i++)
if(s[i]=='(')
{
pos[++nl]=i;
num[nl]=nr;
}
else
{
nr++;
sum[nr]=sum[nr-1]+val[i];
}
memset(dp,0,sizeof(dp));
for(int i=nl;i>=1;i--)
{
for(int j=n-(nl-i);j>=pos[i];j--)
{
ll temp=(ll)val[pos[i]]*(sum[num[i]+j-pos[i]]-sum[num[i]]);
dp[i][j]=dp[i+1][j+1]+temp;
if(j<n-(nl-i))dp[i][j]=max(dp[i][j],dp[i][j+1]);
}
for(int j=pos[i]-1;j>=pos[i-1];j--)dp[i][j]=dp[i][j+1];
}
ll ans=0;
for(int j=1;j<=n;j++)ans=max(ans,dp[1][j]);
printf("%lld\n",ans);
}
return 0;
}