题意:
给出一个算式,由括号和小于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]);
}