1. 程式人生 > >hdu 6299 貪心+分治

hdu 6299 貪心+分治

題意:


求n個字串任意排列後可以得到的最大的括號匹配個數

解法:

每個字串內部的匹配數是定值,可以讀入的時候處理一下。處理之後得到的是形如")))((("這樣的串,用a和b分別存每個字串剩下的左括號和右括號的個數,這樣字串就可以丟掉,省很多空間。。

可以發現一個貪心的規律:顯然最後應該是  ))((((( + )))(((((( + )))))(( + ))))(( 這樣的形式是最優的,即分成兩個區間,左邊是左括號多的,右邊是右括號多的。而區間內部的順序對於另一個區間是無所謂的,所以只要考慮區間內部最大化匹配數:對於左區間,應該讓右括號多的儘量靠右;對於右區間則相反。

這樣我們定義了一種能得到最優解的偏序關係,將字串按照這個關係排序,統計答案即可。

#include<bits/stdc++.h>
using namespace std;

#define llp(i,x,y) for(int i=x;i<y;++i) // [x,y) x->y
#define rlp(i,x,y) for(int i=y-1;i>=x;--i)  // [x,y) y->x
#define lp(i,x) for(int i=0;i<x;++i) //[0,x)0->x
#define mem(a,x) memset(a,x,sizeof a)

typedef long long ll;

const ll N=1e5+1000;

struct node{
  ll a,b;
  ll ans;
  bool operator < (node& x){
    int tmp1 = a>b;
    int tmp2 = x.a>x.b;
    if (tmp1!=tmp2) return tmp1<tmp2;
    else{
      return tmp1==1?b>x.b:a<x.a;
    }
  }
}Str[N];
char s[N];
void deal(int i){
  int len = strlen(s);
  Str[i].a = Str[i].b = Str[i].ans = 0;
  llp(k,0,len){
    if (s[k] == '(') Str[i].b++;
    else if (Str[i].b>0) {
      Str[i].ans+=2;
      Str[i].b--;
    }
    else Str[i].a++;
  }
}
int main(){
  int t,n;
  scanf("%d",&t);
  while(t--){
    scanf("%d",&n);
    lp(i,n) {
      scanf("%s",s);
      deal(i);
    }

    sort(Str,Str+n);//核心操作


    ll ans=0;
    ll left = 0;
    lp(i,n){
      ans += Str[i].ans + 2*min(left,Str[i].a);
      left = max(left - Str[i].a,0ll);
      left += Str[i].b;
    }
    printf("%lld\n",ans);
  }
}