括号序列的dp问题模型

括号序列的dp问题模型

Codeforces314E

◦给定一个长度为n的仅包含左括号和问号的字符串,将问号变成左括号或

右括号使得该括号序列合法,求方案总数。

◦例如(())与()()都是合法的括号序列。

◦ n<=3000。

在括号序列问题中,总是把左括号看作+1,右括号看作-1,要使括号序列合法,只需满足任意一个前缀和大于

或等于0,且总和为0即可

设dp[i][j]表示第i个字符,还剩下j个左括号的方案数
若第i+1个字符是左括号,则dp[i+1][j+1]+=dp[i][j]
若第i+1个字符是右括号,则dp[i+1][j-1]+=dp[i][j]
若第i+1个字符是问号,则dp[i+1][j+1]+=dp[i][j],dp[i+1][j-1]+=dp[i][j]
答案为:dp[n][0]

BZOJ3709

◦在一款电脑游戏中,你需要打败n只怪物(从1到n编号)。为了打败第i只

怪物,你需要消耗d[i]点生命值,但怪物死后会掉落血药,使你恢复a[i]点

生命值。任何时候你的生命值都不能降到0(或0以下)。

◦请问是否存在一种打怪顺序,使得你可以打完这n只怪物而不死掉。

◦N<=10^5

巧妙的贪心题目(之所以引入这道贪心,是因为这道题的解题思路对下一题有帮助)

引入zhhx的一句话:

贪心:noip的贪心很多都是按照某种方式排序,然后依次选或处理。

显然,当a[i]>d[i]时,打第i只怪物会回血,所以先打能回血的怪物

在能回血的怪物里,我们需要先打d[i]小的怪物,故按照d[i]从小到大排序

那么对于不能回血的怪物,我们该怎样排序呢?

首先,如果我们最后能活下来,那么剩余的生命值一定是固定的,所以反着来考虑,和正着是一样的

反着来想我们需要先打a[i]小的怪物,正着来想就是按照a[i]从大到小排序

因此我们把这些怪物分成两组,a[i]-d[i]>0的为一组,a[i]-d[i]<0的为一组

再分别按照d[i]从小到大,a[i]从大到小进行排序

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define Maxn 100100
#define ll long long
int n;
ll z;
struct node{
     int dec,inc,num;
}a[Maxn],b[Maxn];
bool cmp1(node x,node y)
{
    return x.dec<y.dec;
}
bool cmp2(node x,node y)
{
    return x.inc>y.inc;
}
int ans[Maxn];
int tot1=0,tot2=0;
int main()
{
    scanf("%d%lld",&n,&z);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(y-x>=0)//
        {
            a[++tot1].dec=x;a[tot1].inc=y;a[tot1].num=i;
        }
        else
        {
            b[++tot2].dec=x;b[tot2].inc=y;b[tot2].num=i;
        }
    }
    sort(a+1,a+tot1+1,cmp1);
    sort(b+1,b+tot2+1,cmp2);
    for(int i=1;i<=tot1;i++)
    {
        z-=a[i].dec;
        if(z<=0){printf("NIE");return 0;}
        z+=a[i].inc;
    }
    for(int i=1;i<=tot2;i++)
    {
        z-=b[i].dec;
        if(z<=0){printf("NIE");return 0;}
        z+=b[i].inc;
    }
    printf("TAK\n");
    for(int i=1;i<=tot1;i++)printf("%d ",a[i].num);
    for(int i=1;i<=tot2;i++)printf("%d ",b[i].num);
    return 0;
}
上一篇:[题解] P1342 请柬


下一篇:生成树,2019NOI金牌营6,思维好题