【bzoj4922】[Lydsy六月月赛]Karp-de-Chant Number 贪心+背包dp

题目描述

给出 $n$ 个括号序列,从中选出任意个并将它们按照任意顺序连接起来,求以这种方式得到匹配括号序列的最大长度。

输入

第一行包含一个正整数n(1<=n<=300),表示括号序列的个数。

接下来n行,每行一个长度在[1,300]之间的括号序列,仅由小括号构成。

输出

输出一行一个整数,即最大长度,注意你可以一个序列也不选,此时长度为0。

样例输入

3
())
((()
)()

样例输出

10



题解

贪心+背包dp

首先对于一个括号序列,有用的只有:长度、消耗‘(‘的数目、以及‘(‘减去‘)‘的数目。

显然可以dp,但是由于顺序对于本题来说时有用的,因此不能直接dp。

进一步思考可以发现:本题和【bzoj4619】[Wf2016]Swap Space 相同,因此可以按照那道题的贪心策略来决定选择的顺序。

即:先选择‘(‘多于‘)‘的,对于‘(‘多于‘)‘的按照消耗‘(‘的数目从小到大排序,否则按照多出来‘(‘(即‘(‘减去‘)‘的数目+消耗‘(‘的数目)从大到小排序。

确定了顺序后就好办了。设 $f[i][j]$ 表示排序后前 $i$ 个括号序列,多出来‘(‘ 的数目为 $j$ 的最大长度。那么这是一个背包问题。当 $j$ 大于等于消耗 ‘(‘ 的数目时能够转移。

最后的答案就是 $f[n][0]$ 。

时间复杂度 $O(n^3)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 310
using namespace std;
struct data
{
    int x , y , z;
    bool operator<(const data &a)const
    {
        if(y > 0 && a.y <= 0) return 1;
        if(y < 0 && a.y >= 0) return 0;
        if(y > 0) return x < a.x;
        return x + y > a.x + a.y;
    }
}a[N];
int f[N][N * N];
char str[N];
int main()
{
    int n , m = 0 , i , j;
    scanf("%d" , &n);
    for(i = 1 ; i <= n ; i ++ )
    {
        scanf("%s" , str) , a[i].z = strlen(str) , m += a[i].z;
        for(j = 0 ; j < a[i].z ; j ++ )
            a[i].y += (str[j] == ‘(‘ ? 1 : -1) , a[i].x = max(a[i].x , -a[i].y);
    }
    sort(a + 1 , a + n + 1);
    memset(f , 0xc0 , sizeof(f)) , f[0][0] = 0;
    for(i = 1 ; i <= n ; i ++ )
    {
        for(j = 0 ; j <= m ; j ++ ) f[i][j] = f[i - 1][j];
        for(j = a[i].x ; j <= m ; j ++ )
            if(j + a[i].y >= 0 && j + a[i].y <= m)
                f[i][j + a[i].y] = max(f[i][j + a[i].y] , f[i - 1][j] + a[i].z);
    }
    printf("%d\n" , f[n][0]);
    return 0;
}
时间: 2024-10-17 00:21:34

【bzoj4922】[Lydsy六月月赛]Karp-de-Chant Number 贪心+背包dp的相关文章

【bzoj5072】[Lydsy十月月赛]小A的树 树形背包dp

题解: 比较好想 首先注意到如果最暴力的做法复杂度无法接受 而5000的范围基本是n^2做法了 只使用已经遍历过的点数目和当前子树中的点数目转移我们知道复杂度是n^2的 于是大胆猜测一波同一个节点为根值域是连续的 然后就可以暴力dp了 查询的时候我们可以把序列差分一下(原问题等价于 区间修改,区间询问) 其实更强一点的结论是 对于整棵树,值域都是连续的 但我感觉这个我并不会证(网上代码好像基本都是用这个结论的) 代码: 原文地址:https://www.cnblogs.com/yinwuxiao

【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并

[BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Input 第一行包含一个正整数n(1<=n<=200000),表示节点的个数. 接下来n行,每行两个整数v

[BZOJ4920][Lydsy六月月赛]薄饼切割

[BZOJ4920][Lydsy六月月赛]薄饼切割 试题描述 有一天,tangjz 送给了 quailty 一张薄饼,tangjz 将它放在了水平桌面上,从上面看下去,薄饼形成了一个 \(H \times W\) 的长方形. tangjz 交给了 quailty 一根木棍,要求 quailty 将木棍轻轻放到桌面上.然后 tangjz 会以薄饼中心作为原点,将木棍绕着原点旋转一圈,将木棍扫过的部分切下来送给 quailty. quailty 已经放好了木棍,请写一个程序帮助他们计算 quailt

bzoj 4921: [Lydsy六月月赛]互质序列

4921: [Lydsy六月月赛]互质序列 Time Limit: 1 Sec  Memory Limit: 256 MBSubmit: 188  Solved: 110[Submit][Status][Discuss] Description 你知道什么是"互质序列"吗?那就是所有数的最大公约数恰好为1的序列. "互质序列"非常容易找到,但是我们可以尝试通过删除这个序列的一个非空连续子序列来扩大它的最大公约数. 现在给定一个长度为n的序列,你需要从中删除一个非空连

bzoj 4919: [Lydsy六月月赛]大根堆

Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Solution 思路比较直接 设 \(f[i][j]\) 表示\(i\)为子树的节点中,堆中最大值小于\(j\)的情况下能选的最多点数 转移时就是用一个前缀最大值更

【BZOJ4919】[Lydsy六月月赛]大根堆

题解: 首先裸的dp很好想 f[i][j]表示在i点,最大值<=j的点数最大值 看了别人的题解知道了可以用线段树合并来优化这个东西.. 我们考虑对于每个点,首先我们要合并它的子树 其实就是对于相同位置的点相加即可 然后考虑当前节点,我们应用f[v[x]-1]+1去更新v[x]-n之间的值(也就是取max操作) 然后取max这个东西是可以标记永久化的 原文地址:https://www.cnblogs.com/yinwuxiao/p/9060320.html

【BZOJ4953】lydsy七月月赛 F DP

[BZOJ4953]lydsy七月月赛 F 题面 题解:设f[i][j]表示第i个强度取为j时的最小误差.那么每次转移时,我们只计算j'和j之间的像素点带来的误差,于是有: $f[i][j]=min(f[i-1][k]+g(k..mid,k)+g(mid+1...j,j))|mid={k+j\over 2}$ 其中,&g(a,b)=P_a*(a-b)^2\\=P_a*a*a-2*P_a*a*b+2*P_a*b*b& 于是维护P_a*a*a,P_a*a,P_a的前缀和即可. #include

【BZOJ4950】lydsy七月月赛 C 二分图最大匹配

[BZOJ4950]lydsy七月月赛 C 题面 题解:比较直接的想法就是:每行,每列的最大值都留下,剩下的格子都变成1.但是如果一个格子既是行的最大值又是列的最大值,那么我们只需要把它留下即可.这就变成了一个二分图最大匹配问题,乱搞即可. #include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; int n,m,cnt,now;

【BZOJ4952】lydsy七月月赛 E 二分答案

[BZOJ4952]lydsy七月月赛 E 题面 题解:傻题...二分答案即可,精度有坑. #include <cstdio> #include <cstring> #include <iostream> using namespace std; double eps=1e-9; int n,t; int d[1010],s[1010]; bool solve(double x) { double ret=0; for(int i=1;i<=n;i++) ret+