2018浙江省赛 括号序列

题意

你有 n 个元素排成一行,每个元素都由一个括号 (左括号或右括号) 和一个权值构成,我们将第 i 个元素记作(si; vi),其中 si 为 “(” 或者 “)”,vi 为一个整数 (可能为负数)。

你每次可以选择一对相邻的元素,对应的括号为 “()”,即找到一个 k(1 k < n),满足 sk 为 “(” 且 sk+1 为“)”。你可以交换第 k 和 k + 1 个元素 (包括括号和对应的权值),然后获得 vk vk+1 的分数。

现在给你初始的元素排列,问你最多能获得多少的分数。


如果所有数都>0的话,那贪心把所有)都移到最左边就好了,但是这里有负数。

首先可以发现

1.答案只取决于原始状态和最终状态[可以确定每个')'跨越了几个'(']

2.原序列中一个右括号右边的右括号不可能向左移时跨越它

所以dp[i][j]表示到左数第i个‘)’时,最终将它放在第j个‘(’后面,最大收益多少

看起来好像要枚举dp[i-1][k](0<=k<=j),发现每次转移都是dp[i-1],[0-j]+v[j--当前‘(’数量],这样可以维护前缀最大值和前缀和优化一下,把dp转移降到O(1)

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
char s[2020];
LL dp[2020][2020],A[2020],sum[2200];
int len,n,m;
LL Max(LL a,LL b){
	if (a>b) return a;
	return b;
}
void Work(){
	memset(sum,0,sizeof(sum));
	n=m=0;
	int i,j,mz=0;
	scanf("%d",&len);
	for (i=1;i<=len;i++){
		cin>>s[i];
		if (s[i]=='(') mz++;
	}
	for (i=1;i<=len;i++)
		scanf("%lld",&A[i]);
	
	for (i=1;i<=len;i++){
		if (s[i]=='('){
			m++;
			sum[m]=sum[m-1]+A[i];
			continue;
		}
		n++;
		for (j=0;j<=m;j++){
			dp[n][j]=dp[n-1][j]+(sum[m]-sum[j])*A[i];
			if (j) dp[n][j]=Max(dp[n][j],dp[n][j-1]);
		}
		for (j=m+1;j<=mz;j++) dp[n][j]=dp[n][j-1];//以后转移要用
	}
	cout<<dp[n][m]<<endl;
}
int main(){
	int num; cin>>num;
	while (num--) Work();
}

dp[i][j]只从dp[i-1][j]转移过来,虽然本题空间不需要优化,但我们发现可以优化掉一维,这样的话dp数组需要初始化

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
char s[2020];
LL dp[2020],A[2020],sum[2200];
int len,n,m;
LL Max(LL a,LL b){
	if (a>b) return a;
	return b;
}
void Work(){
	memset(sum,0,sizeof(sum));
	memset(dp,0,sizeof(dp));
	n=m=0;
	int i,j,mz=0;
	scanf("%d",&len);
	for (i=1;i<=len;i++){
		cin>>s[i];
		if (s[i]=='(') mz++; 
	}
	for (i=1;i<=len;i++)
		scanf("%lld",&A[i]);
	
	for (i=1;i<=len;i++){
		if (s[i]=='('){
			m++;
			sum[m]=sum[m-1]+A[i];
			continue;
		}
		n++;
		for (j=0;j<=m;j++){
			dp[j]=dp[j]+(sum[m]-sum[j])*A[i];
			if (j) dp[j]=Max(dp[j],dp[j-1]);
		}
		for (j=m+1;j<=mz;j++) dp[j]=dp[j-1]; 
	}
	cout<<dp[m]<<endl;
}
int main(){
	int num; cin>>num;
	while (num--) Work();
}

猜你喜欢

转载自blog.csdn.net/lerbon23james/article/details/80230926