ZOJ 4027 Sequence Swapping(DP+思維)
阿新 • • 發佈:2019-01-02
大致題意:給你一些括號,有左括號有右括號,每一個括號對應一個數值vi。當左右括號i、j相鄰並且左括號在左、右括號在右,你可以選擇交換這兩個括號的位置,並且產生一個vi*vj的權值。交換次數不限,現在問你能夠產生的最大權值和是多少。 首先,對於左括號來說,如果往右移了一位,即與某一個右括號交換了,那麼就一定不會交換回來。這是一個很明顯的無後效性,因此考慮dp。但是有另外一個問題,每一次的交換會對括號的序列發生改變,直接dp可能又會產生後效性。所以得從最後結果來考慮。 容易知道,由於交換一定是要左右括號配上之後才能夠交換,所以左後的結果相當於是,所有左括號內部的相對位置不變,所有右括號內部的相對位置也不變。於是,我就可以不考慮中間的順序,我直接考慮每個最後左括號相對所有右括號的位置。如果初始時某個左括號右邊有i個右括號,最後又j個右括號,其中j<=i,那麼產生的代價就是vi*(s[i]-s[j]),其中s表示右括號權值的字尾和。 是固定的,每次轉移只需要找最大的dp[i-1][k]即可,而這個最大值也是可以簡單的維護的。總的複雜度可以做到O(N^2)。具體見程式碼:
如此一來,我們令dp[i][j]表示第i個左括號,最終的在第j個括號右邊的最大權值和。於是可以得到轉移方程dp[i][j]=max(dp[i-1][k]+s[pos[i]]-s[j+1]),其中pos[i]表示左括號i的初始位置,s表示右括號的字尾和。可以看到這個時間複雜度是O(N^3)的,但是顯然,由於增加值s[pos[i]]-s[j+1]
#include<bits/stdc++.h> #define INF 0x3f3f3f3f #define LL long long #define N 1010 using namespace std; LL sum[N],v[N],w[N][N],dp[N][N]; char s[N]; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T; cin>>T; while(T--) { int m=0,n; cin>>n>>s+1; LL tot=0,t=0,ans=0; memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) cin>>v[i]; for(int i=1;i<=n;i++) if (s[i]==')') sum[++tot]=v[i]; for(int i=tot-1;i>=1;i--) sum[i]+=sum[i+1]; for(int i=0;i<=n;i++) for(int j=0;j<=tot+1;j++) dp[i][j]=w[i][j]=-INF; memset(w[0],0,sizeof(w[0])); for(int i=n;i>=1;i--) { if (s[i]==')') {t++;continue;} m++; for(int j=tot;j>=tot-t;j--) { dp[m][j]=w[m-1][j]+(sum[tot-t+1]-sum[j+1])*v[i]; ans=max(dp[m][j],ans); } for(int j=tot;j>=0;j--) w[m][j]=max(w[m][j+1],dp[m][j]); } cout<<ans<<endl; } }