Polygon

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');
}

猜你喜欢

转载自www.cnblogs.com/a1b3c7d9/p/10924095.html