競賽題解 - Karp-de-Chant Number(BZOJ-4922)

Karp-de-Chant Number(BZOJ-4922) - 競賽題解

進行了一次DP的練習,選幾道題寫一下博客~
標籤:BZOJ / 01揹包 / 貪心php


『題目』

>> 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揹包 的過程了——排序

\[dp[i][j]=\begin{cases} \min(dp[i-1][j],dp[i-1][j-del]+len)\ \ \ \ |\ j\ge del , j-del+Mindel\ge 0\\ dp[i-1][j]\ \ \ |\ else \end{cases} \]

至於「\(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;
}

\(\mathcal{The\ End}\)

\(\mathcal{Thanks\ For\ Reading!}\)

關於博客裏面的問題各位能夠在 \(lucky\_glass@foxmail.com\) e-mail一下~get

相關文章
相關標籤/搜索