一、题目
二、解法
首先可以把这个括号算式化成一棵二叉树,底层是数值,非叶节点是符号,构造方法:
- 维护一个管辖当前部分的节点
p
r
e
pre
pre,当遇到
(
或者?
的时候新建节点并且把这个节点当做 p r e pre pre的儿子,新建的节点成为新的 p r e pre pre,因为遇到这两个字符有进入到了一个更小的区域,有新的管辖点。 - 如果遇到
)
或者数值,那么当前区域结束,返回上一区域, p r e = f a [ p r e ] pre=fa[pre] pre=fa[pre] - 特别地,如果遇到了数值,需要给当前点赋权值
然后就可以跑 d p dp dp了,注意这道题 min ( p , m ) = 100 \min(p,m)=100 min(p,m)=100,所以可以定义 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]为在节点 i i i的子树内,用了 j j j个加号 / / /减号(数量少的塞状态),所获得的最大值 / / /最小值,转移就枚举左右儿子选的个数。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 105;
const int M = 10005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,p,m,k,tot,pre,f[2][M][N],fa[M],son[M][2];char s[M];
void dfs(int u)
{
if(!u) return ;
dfs(son[u][0]);dfs(son[u][1]);
for(int i=0;i<=k;i++)
for(int j=0;i+j<=k;j++)
{
f[0][u][i+j+(p>=m)]=max(f[0][u][i+j+(p>=m)],f[0][son[u][0]][i]-f[1][son[u][1]][j]);
f[1][u][i+j+(p>=m)]=min(f[1][u][i+j+(p>=m)],f[1][son[u][0]][i]-f[0][son[u][1]][j]);
f[0][u][i+j+(p<m)]=max(f[0][u][i+j+(p<m)],f[0][son[u][0]][i]+f[0][son[u][1]][j]);
f[1][u][i+j+(p<m)]=min(f[1][u][i+j+(p<m)],f[1][son[u][0]][i]+f[1][son[u][1]][j]);
}
}
signed main()
{
scanf("%s",s+1);
n=strlen(s+1);
tot=pre=1;
memset(f[0],-0x3f,sizeof f[0]);
memset(f[1],0x3f,sizeof f[1]);
for(int i=1;i<=n;i++)
{
if(s[i]=='(' || s[i]=='?') son[pre][son[pre][0]?1:0]=++tot,fa[tot]=pre,pre=tot;
else if(s[i]==')') pre=fa[pre];
else f[0][tot][0]=f[1][tot][0]=s[i]-'0',pre=fa[pre];
}
p=read();m=read();k=min(p,m);
dfs(1);
printf("%d\n",f[0][1][k]);
}