CF935E Fafa and Ancient Mathematics

一、题目

点此看题

二、解法

首先可以把这个括号算式化成一棵二叉树,底层是数值,非叶节点是符号,构造方法:

  • 维护一个管辖当前部分的节点 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 
#include 
#include 
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]);
}

你可能感兴趣的:(树形dp)