习题:Fafa and Ancient Mathematics(树DP&转换问题)
题目
思路
一个括号其实就相当于一个子树
也就是这是一个树形DP的题
这题的难点也在于转换,DP并不是很难
\(dp[i][j][0/1]\)表示以i为根节点的子树,用了j的\(-/+\)之后的最大/最小值
用类似于背包的方程去转移其即可
代码
#include<iostream>
#include<cstring>
using namespace std;
char a[100005];
int dp[100005][105][2];//用加号作为限制,0/1:最小值&最大值
int f[100005][105][2];//用减号作为限制,0/1:最小值&最大值
int lena;
int limit,p,m;
int tre[10005][2],cnt,pre,fa[10005];
void dfs(int u)
{
if(tre[u][0]==0)
return;
dfs(tre[u][0]);
dfs(tre[u][1]);
//cout<<u<<' '<<tre[u][0]<<endl;
//cout<<u<<' '<<tre[u][1]<<endl;
for(int i=0;i<=limit;i++)
{
for(int j=0;j<=i;j++)
{
//处理加号
dp[u][i+1][0]=min(dp[u][i+1][0],dp[tre[u][0]][j][0]+dp[tre[u][1]][i-j][0]);
dp[u][i][0]=min(dp[u][i][0],dp[tre[u][0]][j][0]-dp[tre[u][1]][i-j][1]);
dp[u][i+1][1]=max(dp[u][i+1][1],dp[tre[u][0]][j][1]+dp[tre[u][1]][i-j][1]);
dp[u][i][1]=max(dp[u][i][1],dp[tre[u][0]][j][1]-dp[tre[u][1]][i-j][0]);
//处理减号
f[u][i][0]=min(f[u][i][0],f[tre[u][0]][j][0]+f[tre[u][1]][i-j][0]);
f[u][i+1][0]=min(f[u][i+1][0],f[tre[u][0]][j][0]-f[tre[u][1]][i-j][1]);
f[u][i][1]=max(f[u][i][1],f[tre[u][0]][j][1]+f[tre[u][1]][i-j][1]);
f[u][i+1][1]=max(f[u][i+1][1],f[tre[u][0]][j][1]-f[tre[u][1]][i-j][0]);
}
}
}
int main()
{
ios::sync_with_stdio(false);
for(int i=0;i<=10000;i++)
{
for(int j=0;j<=100;j++)
{
f[i][j][0]=dp[i][j][0]=(1<<25);
f[i][j][1]=dp[i][j][1]=-(1<<25);
}
}
cin>>(a+1);
lena=strlen(a+1);
cin>>p>>m;
limit=min(p,m);
cnt=pre=1;
for(int i=1;i<=lena;i++)
{
if(a[i]=='('||a[i]=='?')
{
tre[pre][tre[pre][0]?1:0]=++cnt;
fa[cnt]=pre;
pre=cnt;
}
else if(a[i]==')')
pre=fa[pre];
else
{
dp[cnt][0][0]=dp[cnt][0][1]=a[i]-'0';
f[cnt][0][0]=f[cnt][0][1]=a[i]-'0';
pre=fa[pre];
}
}
dfs(1);
if(p<m)
cout<<dp[1][p][1];
else
cout<<f[1][m][1];
return 0;
}

浙公网安备 33010602011771号