Sequence Swapping
Time Limit: 1000 ms Memory Limit: 65536 KB
题意
给出一个括号序列,每个括号对应一个值,当相邻两个括号k为’ ( ‘,k+1为’ ) '时可以交换,得到括号对应值乘积的贡献,可以进行任意次数的交换,求得到的最大贡献值。
范围
( 1 ≤ n ≤ 10^3 , −10 ^ 3 ≤ vi ≤ 10 ^ 3 )
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
思路借鉴
链接
https://blog.csdn.net/V5ZSQ/article/details/80205265
Solution
线性dp。
设dp[i][j]为将第i个左括号移动到j位置或j的右边所得到的最大价值。
对于某个左括号,只有其右边的左括号把右括号换过来,它才能与其交换,所以要把第i个换到j位置,必须保证i+1已经在j+1位置或者它的右侧,很明显,i和j都需要倒序枚举。实质上就是dp[i][j]=将i移到j位置的收益+max(dp[i+1][j+1…n])。
dp[i][j] = max(dp[i][j + 1],dp[i + 1][j + 1] + i 换到 j 的收益)
答案在dp[1][1,2…n] 里。
代码
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int SZ = 1000 + 20;
const int INF = 0x3f3f3f3f;
ll dp[SZ][SZ],val[SZ],pos[SZ],sum[SZ],num[SZ];
ll templ,tempr,ans,n;
char s[SZ];
int main()
{
int T;
scanf("%d",&T);
while(T -- )
{
memset(dp,0,sizeof(dp));
scanf("%lld",&n);
scanf("%s",s + 1);
templ = tempr = 0;
ans = 0;
for(int i = 1;i <= n;i ++ ) scanf("%lld",&val[i]);
for(int i = 1;i <= n;i ++ )
{
if(s[i] == '(' )
{
pos[ ++ templ] = i;
num[templ] = tempr;
}
else sum[ ++ tempr] = sum[tempr - 1] + val[i];
}
for(int i = templ;i >= 1;i -- )
{
for(int j = n - (templ - i);j >= pos[i];j -- )
{
if(j != n - (templ - i)) dp[i][j] = max(dp[i + 1][j + 1] + val[pos[i]] * (sum[num[i] + j - pos[i]] - sum[num[i]]),dp[i][j + 1]);
else dp[i][j] = dp[i + 1][j + 1] + val[pos[i]] * (sum[num[i] + j - pos[i]] - sum[num[i]]);
if(i == 1) ans = max(ans,dp[i][j]);
}
for(int j = pos[i] - 1;j >= pos[i - 1];j -- )
dp[i][j] = dp[i][j + 1]; //前面的i转移需要用到
}
printf("%lld\n",ans);
}
return 0;
}
2020.3.17