Polygon
给出一个n个点n条边的环,每条边会有一个运算符号,但只能是\(+,\times\),而点上会有一个数字,现在的操作第一步,选择一条边丢掉,接下来的所有操作,每次可以选择条边,删去,并把这删去的边所连的两个点合并称一个顶点,数字为删去边的两个点的数字进行删去的边上的符号,现在询问剩下的最后一个点上的数字的最大值,\(n\leq 50\)。
解
实际上注意到数据范围很下,于是我们先拆一条边,就是一条链了,接着合并两个相邻的顶点,于是发现这应该是一个区间合并问题,而环我们可以利用拆环成链中的再补一截的方法优化。
注:点的编号起点随你定,因为这是一个环
又注意到加法是恒满足最优子结构性质,但是乘法不满足,原因在于最小值也能组成最优解,所以要分类讨论。
因此设\(f[0/1][l][r]\)分别表示合并点l到r的剩下的一个点最小值和最大值,于是不难有
\[if(+),f[0][l][r]=\min_{k=l}^{r-1}(f[0][l][k]+f[0][k+1][r])
\]
\[if(\times),f[0][l][r]=\min_{k=l}^{r-1}\min_{p=q=0}^1(f[p][l][k]\times f[q][k+1][r])
\]
\[if(+),f[1][l][r]=\max_{k=l}^{r-1}(f[1][l][k]+f[1][k+1][r])
\]
\[if(\times),f[1][l][r]=\max_{k=l}^{r-1}\max_{p=q=0}^1(f[p][l][k]\times f[q][k+1][r])
\]
边界:\(f[0][i][i]=f[1][i][i]=\)第i个点上的数字,\(i=1,2,...,n\),f[0][][]无限大,f[1][][]无限小
答案:\(\max_{i=1}^nf[1][i][i+n-1]\)
参考代码:
阶段实现
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
#define llmax 1e16
using namespace std;
char c[101];
ll dp[2][101][101];
int stack[101],st;
il void get(char&);
template<class free>il free Max(free,free);
template<class free>il free Min(free,free);
int main(){
int n,n2;scanf("%d",&n),n2=n<<1;
memset(dp[0],66,sizeof(dp[0]));
memset(dp[1],-66,sizeof(dp[1]));
for(int i(1);i<=n;++i)
get(c[i]),scanf("%lld",&dp[0][i][i]),
c[i+n]=c[i],dp[1][i][i]=dp[1][i+n][i+n]
=dp[0][i+n][i+n]=dp[0][i][i];
for(int i,j(1),k,p,q;j<=n2;++j)
for(i=j-1;i;--i)
for(k=i;k<j;++k)
if(c[k+1]=='t')
dp[0][i][j]=Min(dp[0][i][j],dp[0][i][k]+dp[0][k+1][j]),
dp[1][i][j]=Max(dp[1][i][j],dp[1][i][k]+dp[1][k+1][j]);
else for(p=0;p<2;++p)
for(q=0;q<2;++q)
dp[0][i][j]=Min(dp[0][i][j],dp[p][i][k]*dp[q][k+1][j]),
dp[1][i][j]=Max(dp[1][i][j],dp[p][i][k]*dp[q][k+1][j]);
ll ans(-llmax);
for(ri int i(1);i<=n;++i)
if(dp[1][i][i+n-1]>ans)
ans=dp[1][i][i+n-1],stack[st=1]=i;
else if(dp[1][i][i+n-1]==ans)
stack[++st]=i;
printf("%lld\n",ans);
for(int i(1);i<=st;++i)
printf("%d ",stack[i]);
return 0;
}
template<class free>
il free Max(free a,free b){
return a>b?a:b;
}
template<class free>
il free Min(free a,free b){
return a<b?a:b;
}
il void get(char&c){
while(c=getchar(),c==' '||c=='\r'||c=='\n');
}
dfs实现
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define ll long long
#define llmax 1e16
#define lsy akioi tql
using namespace std;
char c[101];
int stack[101],st;
ll dp[2][101][101];
bool deal[2][101][101];
il void get(char&);
il ll dfs(int,int,int);
template<class free>il free comp(int,free,free);
int main(){
int n,i;scanf("%d",&n);
for(i=1;i<=n;++i)
get(c[i]),c[i+n]=c[i],
scanf("%lld",&dp[0][i][i]),
dp[1][i][i]=dp[0][i+n][i+n]
=dp[1][i+n][i+n]=dp[0][i][i],
deal[0][i][i]=deal[1][i][i]=
deal[0][i+n][i+n]=deal[1][i+n][i+n]=true;
ll ans(-llmax);
for(int i(1);i<=n;++i)
if(ans<dfs(1,i,i+n-1))
ans=dfs(1,i,i+n-1),stack[st=1]=i;
else if(ans==dfs(1,i,i+n-1))stack[++st]=i;
printf("%lld\n",ans);for(int i(1);i<=st;++i)printf("%d ",stack[i]);
return 0;
}
template<class free>
il free comp(int p,free a,free b){
return (p^(a<b))?a:b;
}
il ll dfs(int p,int l,int r){
if(deal[p][l][r])return dp[p][l][r];
dp[p][l][r]=p?-llmax:llmax,deal[p][l][r]|=true;
for(int k(l),a,b;k<r;++k)
if(c[k+1]=='t')
dp[p][l][r]=comp(p,dp[p][l][r],dfs(p,l,k)+dfs(p,k+1,r));
else for(a=0;a<2;++a)
for(b=0;b<2;++b)
dp[p][l][r]=comp(p,dp[p][l][r],dfs(a,l,k)*dfs(b,k+1,r));
return dp[p][l][r];
}
il void get(char &c){
while(c=getchar(),c==' '||c=='\n'||c=='\r');
}