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
#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

你可能感兴趣的:(线性dp,动态规划求解)