1. 程式人生 > >2018浙江省賽 括號序列

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();
}