polygon题解-区间DP

Polygon

https://ac.nowcoder.com/acm/problem/105776

#include <iostream>
#include <string.h>
using namespace std;
const int N=55;
typedef long long LL;
char ope[N<<1];
int dp[N<<1][N<<1],a[N<<1],res[N],cnt=0,ans=-0x3f3f3f3f,dp1[N<<1][N<<1];//最开始WA以为爆LL 或者inf了
/*
原本以为是水题,结果把自己坑了··
这个负数接触的太少了··  负数*负数得到的正数可能更大

思路:区间DP,令dp[i][j]表示把从i到j的部分合并之后得到的最大值
    每次去掉所有边中的一条边,考察按照剩下的所有边合并之后能得到的最大值
    因为数据范围只有50,所以直接暴力枚举去掉的边,时间复杂度O(n^4)
    dp方程很简单,dp[i][j]=max(dp[i][j],dp[i][k] _ dp[k+1][j]);
    看k和k+1之间的操作符是乘法还是加法即可
    但是维护最大值的过程中,可能出现两个负数的乘积比其他情况都要大的情况
    所以使用一个dp1[i][j]维护最小值
    在进行乘法更新dp的过程中,检查两个dp1最小值的乘积是不是比dp最大值要大

    然后是日常维护,dp的维护就是找最大值,
    求和时必然时两个最大值之和
    乘积时可能是最大值的乘积,也可能是两个最小值的乘积

    最小值dp1的维护稍微麻烦,
    求和时必然时两个最大值之和
    乘积时可能是两个最小值的乘积,也可能是一个最大值和一个最小值的乘积

    然后为了维护方便,因为每次去掉一条边之后还得顺次枚举剩下的边,相当于一个环,
    断环为链,开两倍维护即可,省的取模
*/
int main()
{
    //memset(dp,-0x3f,sizeof(dp));
    int n; cin>>n;
    for(int i=0;i<n;i++){
        cin>>ope[i]>>a[i];
        ope[i+n]=ope[i];
        a[i+n]=a[i];
    }
    for(int mov=0;mov<n;mov++){//去掉的边
        memset(dp,-0x3f,sizeof(dp));memset(dp1,0x3f,sizeof(dp1));
        for(int i=0;i<n;i++)
            dp[i][i]=dp[i+n][i+n]=a[i],dp1[i][i]=dp1[i+n][i+n]=a[i];
        for(int len=1;len<=n;len++){
            for(int i=mov;i<n+mov;i++){//更新n行 即所有点
                int j=i+len-1;
                for(int k=i;k<j;k++){
                    if(ope[k+1]=='t'){
                        dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
                        dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]);
                    }
                    else{
                        dp[i][j]=max(dp[i][j],dp[i][k]*dp[k+1][j]);
                        dp[i][j]=max(dp[i][j],dp1[i][k]*dp1[k+1][j]);
                        dp1[i][j]=min(dp1[i][j],dp1[i][k]*dp1[k+1][j]);
                        dp1[i][j]=min(dp1[i][j],dp1[i][k]*dp[k+1][j]);
                        dp1[i][j]=min(dp1[i][j],dp[i][k]*dp1[k+1][j]);
                    }

                }
            }
        }
        if(dp[mov][mov+n-1]>=ans){
            if(dp[mov][mov+n-1]>ans)
                cnt=0;
            ans=dp[mov][mov+n-1];
            res[cnt++]=mov;
        }


//        cout<<"图"<<endl;//输出检查
//        for(int i=0;i<n*2;i++){
//            for(int j=0;j<n*2;j++)
//                printf("%11d ",dp[i][j]);
//            cout<<endl;
//        }
//        cout<<endl;
    }


    cout<<ans<<endl;
    for(int i=0;i<cnt;i++)
        cout<<res[i]+1<<" ";
    cout<<endl;

    return 0;
}
全部评论

相关推荐

Noel_:中石油是这样的 哥们侥幸混进免笔试名单 一看给我吓尿了
点赞 评论 收藏
分享
我已成为0offer的糕手:别惯着,胆子都是练出来的,这里认怂了,那以后被裁应届被拖工资还敢抗争?
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务