传送门

题意:

给出一个算式,由括号和小于10的正整数和问号组成,问号是算式中的符号,现给出原式中加号的个数p和减号的个数m,对于所有填放方式对应的结果,求最大值。

算式长度<=1e4 min(p,m)<=100

Solution:

考试时这种题居然没想到转换模型…好菜啊我

我们可以把这个算式化成一个二叉树,叶子节点是一个数,非叶子节点表示一个符号,当一个非叶子结点填加号时,我们就需要最大化左右子树的值,当一个非叶子结点填减号时,我们就需要最大化左子树值,最小化右子树值

那么就可以用树形dp啦

我们可以用 f[i][j][k][0/1] f [ i ] [ j ] [ k ] [ 0 / 1 ] 表示第i个点及其子树中填了j个加号,k个负号所能得到的最大/最小值,但是这样开数组开不下,发现加号和减号数量可以互相转换,所以去掉加号或减号中的一维即可

建树时学习了某位神犇的非常巧妙的做法

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int f[2][10010][110];//0 max 1 min
char s[10010];
int tr[10010][2],size,n,fa[10010],p,m,minn,pre;
void dfs(int x)
{
    if (!tr[x][0]) return;
    int l=tr[x][0],r=tr[x][1];
    dfs(l),dfs(r);
    for (int i=0;i<=minn;i++)
        for (int j=0;i+j<=minn;j++)
        {
            f[0][x][i+j+(p<m)]=max(f[0][x][i+j+(p<m)],f[0][l][i]+f[0][r][j]);
            f[0][x][i+j+(p>=m)]=max(f[0][x][i+j+(p>=m)],f[0][l][i]-f[1][r][j]);
            f[1][x][i+j+(p<m)]=min(f[1][x][i+j+(p<m)],f[1][l][i]+f[1][r][j]);
            f[1][x][i+j+(p>=m)]=min(f[1][x][i+j+(p>=m)],f[1][l][i]-f[0][r][j]);
        }
}
int main()
{
    scanf("%s",s+1);
    scanf("%d%d",&p,&m);
    memset(f[0],-63,sizeof(f[0]));
    memset(f[1],63,sizeof(f[1]));
    minn=min(p,m);
    n=strlen(s+1);
    size=1;pre=size;
    for (int i=1;i<=n;i++)
    {
        if (s[i]=='('||s[i]=='?')
        {
            tr[pre][tr[pre][0]?1:0]=++size;
            fa[size]=pre;
            pre=size;
        }
        else if (s[i]==')') pre=fa[pre];
        else f[0][size][0]=f[1][size][0]=s[i]-'0',pre=fa[pre];
    }
    dfs(1);
    printf("%d",f[0][1][minn]);
}