The 15th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple / ZOJ4027 Sequence Swapping dp递推
Sequence Swapping
题意:
给出长度为 n 的一组括号,每个括号有一个权值。操作:选择一对相邻的左括号和右括号,且左括号在左,交换这两个括号,得到价值为这两个括号权值的乘积。
现在要你按这个操作,问最后的价值和最大可能是多少?
tags:
明显 dp,但很难想到。。
dp[i][j] 表示当前在第 i 个右括号,且第 i 个右括号与它前面 j 个左括号交换后的最大价值和。
我们可以预处理出第 i 个右括号与第 i-1 个右括号之间有 k 个左括号,那么转移为:
- if(j <= k) dp[i][j] = max({dp[i-1][0], dp[i-1][1]......}) + sum(第 i 个右括号左边 j 个左括号的和) * value[i] ;
- else dp[i][j] = max({dp[i-1][j-k], dp[i-1][j-k+1].....}) + sum(第 i 个右括号左边 j-k 个左括号的和) * value[i] ;
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long ll;
const int N = 1005;
int T, n;
char s[N];
ll val[N], cnt1[N], cnt2[N], sum[N][N], cntL, cntR;
ll dp[N][N], vis[N], mx[N][N];
void Init() {
cntL= cntR = 0;
mes(cnt1, 0); mes(cnt2, 0);
mes(sum, 0);
rep(i,1,N-1) rep(j,0,N-1) mx[i][j] = -1e18;
}
int main()
{
scanf("%d", &T);
while(T--)
{
Init();
scanf("%d%s", &n, s+1);
rep(i,1,n)
{
scanf("%lld", &val[i]);
if(s[i]==')') {
++cntR;
vis[i] = cntR;
int num = 0;
per(j,i-1,1) if(s[j]=='(') {
++num;
sum[cntR][num] = sum[cntR][num-1]+val[j];
}
ll tmp = 0;
per(j,i-1,1) {
if(s[j]==')') break;
else ++tmp;
}
cnt2[cntR] = cnt2[cntR-1]+tmp;
cnt1[cntR] = tmp;
}
}
ll ans = 0;
rep(i,1,n) if(s[i]==')')
{
int id = vis[i];
per(j,cnt2[id],0) {
if(j<=cnt1[id]) {
dp[id][j] = mx[id-1][0] + sum[id][j]*val[i];
}
else {
dp[id][j] = mx[id-1][j-cnt1[id]] + sum[id][j]*val[i];
}
mx[id][j] = max(mx[id][j+1], dp[id][j]);
if(id==cntR) ans = max(ans, dp[id][j]);
}
}
printf("%lld\n", ans);
}
return 0;
}