天天看点

【DP】BZOJ4922 Karp-de-Chant Number

分析:

非常套路:每个括号串可以将其内部的括号匹配掉,最终肯定是左边一段连续的")",右边一段连续的"(" (都可能为0)

然后将其表示为二元组(x,y)

当x>y时,加上当前这个串后,括号层数一定会更大,那么肯定比x<=y的要先放。

而要放一个括号串,其要求必须之前的层数不低于x,那么此时就按照x升序排列就好了。(如果某个方案中,x更大的放在更前面,那么交换两者一定还是合法的)

而x<=y的部分也是这样,将上面的反过来考虑即可:要放一个括号串,要求之后的层数不低于y,那么就按照y降序排列就好了。

排好序之后,就成了个傻逼背包题。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 310
using namespace std;
int dp[2][MAXN*MAXN];
struct node{
	int minv,sumv,len,id;
	bool operator < (const node & a) const {
		if(sumv>=0&&a.sumv<0)
			return 1;
		if(sumv<0&&a.sumv>=0)
			return 0;
		if(sumv>=0&&a.sumv>=0)
			return minv>a.minv;
		return sumv-minv>a.sumv-a.minv;
	}
}blo[MAXN];
int n;
char s[MAXN];
int main(){
	SF("%d",&n);
	for(int i=1;i<=n;i++){
		SF("%s",s);
		int minv=0;
		int sumv=0;
		int len=strlen(s);
		for(int j=0;j<len;j++){
			if(s[j]=='(')
				sumv++;
			else
				sumv--;
			minv=min(minv,sumv);
		}
		blo[i].minv=minv;
		blo[i].sumv=sumv;
		blo[i].len=len;
		blo[i].id=i;
	}
	sort(blo+1,blo+1+n);
//	for(int i=1;i<=n;i++)
//		PF("<%d %d %d %d>\n",blo[i].minv,blo[i].sumv,blo[i].len,blo[i].id);
	int now=0;
	memset(dp,-1,sizeof dp);
	dp[0][0]=0;
	for(int i=1;i<=n;i++){
		now^=1;
		memset(dp[now],-1,sizeof dp[now]);
		for(int j=0;j<=90000;j++){
			int j1=j+blo[i].sumv;
			dp[now][j]=max(dp[now][j],dp[now^1][j]);
			if(j+blo[i].minv>=0&&j1>=0&&j1<=90000&&dp[now^1][j]!=-1)
				dp[now][j1]=max(dp[now][j1],dp[now^1][j]+blo[i].len);
		}
//		for(int j=0;j<=43;j++)
//			PF("[%d %d]",j,dp[now][j]);
//		PF("\n--------------------------\n");
	}
	PF("%d\n",dp[now][0]);
}
           
dp