D - Sequence Swapping
看了一天终于会写了哇咔咔。
题意:给你个长度为n的括号序列,每个括号对应一个数字,每操作你可以选择相邻的左右括号交换它们的位置同时将它们的值相乘,你可以操作任意次,问你它们值相乘的结果的和最大可以是多少啊?
解法:因为每个左括号所能到达最右的位置是固定的,并且左括号与右面的任意一个可以到达的右括号交换,仅仅会影响它们俩者的位置,也就是说,左括号和右括号的相对位置都是不变的。所以我们想对于一个( 来说它可以与它后面相邻的)))这样的序列直接交换,这样可以求它与每个右括号交换位置所能产生的价值然后通过比较找它所能产生的最大贡献,但是它后面如果是))()出现了一个左括号的话,如果它想和第四个位置右括号交换位置那么首先得满足第三个位置的左括号已经和第四个位置的右括号交换了位置,所以,在计算它与第三个右括号交换所能产生的最大价值时,我们可以想到要利用满足第三个位置的左括号已经和第四个位置的右括号交换了位置的这种情况下的最优解去推这个括号能产生的最优解,也就是说,对于每个左括号来说当计算它所能产生的贡献时,需要判断比较它与每个右边括号交换的最大贡献,而计算它与每个右边括号交换的最大贡献时需要考虑他所在右边的所有左括号都已经到了你要交换的这个右边括号的右边位置,所以我们要倒着从右往左来判断每个左括号能产生的最大贡献,又因为,我们在计算每个左括号时都考虑了他右边所有左括号的最优情况,所以最后最左边的左括号得到f[1,j] j从1到n求个最大值就是答案。
#include<bits/stdc++.h> using namespace std; #define LL long long LL f[1005][1005]; LL a[1005]; LL pres[1005]; int l[1005]; char s[1005]; LL v[1005]; int main() { int t; scanf("%d",&t); while(t--) { int n; scanf("%d",&n); scanf("%s",s+1); memset(f,0,sizeof(f)); memset(pres,0,sizeof(pres)); int q=0; for(int i=1; i<=n; i++) { scanf("%lld",&v[i]); if(s[i]=='(') { l[++q]=i; pres[i]=pres[i-1]; } else pres[i]=pres[i-1]+v[i]; } for(int i=q;i>=1;i--) { int pos=l[i]; LL maxx=f[i+1][n]; for(int j=n;j>=pos;j--) { maxx=max(f[i+1][j],maxx); f[i][j]=maxx+(pres[j]-pres[pos])*v[pos]; } } LL ans=0; for(int i=1;i<=n;i++) ans=max(ans,f[1][i]); printf("%lld\n",ans); } }