>> There!
給出 \(n\) 個括號字符串(只包含'(',')'),你須要從中選出一些字符串並對它們排序,使得它們連成一個字符串後構成一個匹配的字符串。求構成的字符串的最大長度~數組
假如咱們不考慮順序,只考慮選擇哪些字符串,咱們很容易想到 dp[i][j] 表示在前 \(i\) 個字符串中選出一些字符串使得 左括號的個數-右括號的個數 爲 \(j\)。那麼顯然結果就是 dp[n][0]~其實還挺像01揹包spa
可是咱們不得不考慮如何安排原來字符串的順序——由於咱們dp轉移時字符串的順序是有影響的(不難理解,在轉移時,咱們要時刻保持‘(’數量≥')'數量,可是假如第一個字符串是")))",第二個字符串是"(((",直接按這種順序咱們就無法選擇)
這樣咱們須要肯定一開始的字符串的順序……還挺像一道貪心題。
咱們先把一個字符串轉換爲一個結構體:code
struct NODE{ int del, //左括號-右括號 Mindel, //對於字符串的每個位置i,求出開頭到i的左括號數量-右括號數量,Mindel是它們的最小值 len; //字符串長度 };
爲何要儲存這些值?顯然是有用的……
第一個貪心性質比較簡單——按 \(del\) 從大到小排序,而後將 \(del\ge 0\) 和 \(del<0\) 分紅先後兩半。
第二個貪心是把前一半按 \(Mindel\) 從大到小排序。由於 \(Mindel\) 越大,就說明剩下來的左括號數量越多,那麼就越能和接下來的右括號匹配(畢竟在轉移時,咱們容許暫時存在左括號數量>右括號數量,可是絕對不能右括號數量>左括號數量!)
第三個貪心是把後一半按 \(del-Mindel\) 從大到小排序。\(del-Mindel\) 是什麼呢?若是咱們把 \(del\) 也當作一個前綴和(也就是從開頭到末尾的前綴和),那麼 \(del-Mindel\) 就能夠當作一個 後綴和 .顯然對於字符串的第 \(i\) 個位置,\(1\)~\(i\) 的前綴和 加上 \(i+1\)~\(len\) 的後綴和 就是 \(del\) ,由於此時的前綴和最小,那麼後綴和就最大。由於del<0,因此Mindel<0,也就是右括號比左括號多,則「後綴和大」說明 (右括號數量-左括號數量) 越小,其實就是多餘的右括號越少。那麼顯然咱們更容易讓少許右括號與多餘的左括號匹配,因此就按 \(del-Mindel\) 排序了。blog
接下來就相似一個 01揹包 的過程了——排序
至於「\(j-del+Mindel\ge 0\)」 就是說若是要選擇第 \(i\) 個字符串,那麼它對 \(j\) 的貢獻應該是 \(del\) ,因此 \(j-del\) 就是上一次沒有選擇第 \(i\) 個字符串時的 \(j\) 。再加上 \(Mindel\) 就是鏈接上第 \(i\) 個字符串後從左到右計算 左括號-右括號 的最小值,若是它爲負數,那麼就存在一個位置到開頭的 左括號 比 右括號 少,就不合法!
稍微一點細節就是注意判斷 \(j-del\) 事後會不會數組越界的問題了~字符串
/*Lucky_Glass*/ #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=300; struct NODE{ int del,Mindel,len; // NODE(){Mindel=(1<<30);} }nod[N+7]; bool cmp1(NODE A,NODE B){return A.del>B.del;} bool cmp2(NODE A,NODE B){return A.Mindel>B.Mindel;} bool cmp3(NODE A,NODE B){return A.del-A.Mindel>B.del-B.Mindel;} int n,Maxdel; int dp[N+7][N*N+7]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ char str[N+7]=""; scanf("%s",str); nod[i].len=strlen(str); for(int j=0;j<nod[i].len;j++) nod[i].del+=(str[j]=='('? 1:-1), nod[i].Mindel=min(nod[i].Mindel,nod[i].del), Maxdel+=(str[j]=='('? 1:0); } sort(nod+1,nod+n+1,cmp1); int pos=n+1; for(int i=1;i<=n;i++) if(nod[i].del<0){ pos=i; break; } sort(nod+1,nod+pos,cmp2); sort(nod+pos,nod+n+1,cmp3); memset(dp,200,sizeof dp); dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=Maxdel;j++){ dp[i][j]=dp[i-1][j]; if(j-nod[i].del>=0 && j-nod[i].del<=Maxdel && j-nod[i].del+nod[i].Mindel>=0) dp[i][j]=max(dp[i][j],dp[i-1][j-nod[i].del]+nod[i].len); } } printf("%d\n",dp[n][0]); return 0; }
關於博客裏面的問題各位能夠在 \(lucky\_glass@foxmail.com\) e-mail一下~get