Zoj 4027 Sequence Swapping (線性dp)

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<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章