POJ - 1179 Polygon 区间dp

题目链接

题意:有一个n个点的简单环。环上每个节点有一个数字,每个节点间的连边上有一个字符'x'或者't',在游戏开始前先删除一条边,然后每次选择一条边删除,若这条边上字符为't'则边的两个节点相加并生成为一个新的节点,'x'的话则相乘,直到只剩下一个节点为止,问剩下的这个节点最大是多少,且先删除哪一条边可以得到这个最大值。

思路:区间dp,每次选择一条边删除,那么我们可以把这些节点放在一个数组a里,然后a[i+n]=a[i],复制一遍该数组,然后枚举区间的起点,i为起点时则是先删除了第i条边。

ps:由于此题运算过程存在负数且有乘法,则可能出现某个区间最大的结果=两个值为负数的子区间相乘,所以我们要再设一个数组用来存区间的最小值。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define PI acos(-1)
#define INF 0x3f3f3f3f
#define NUM 2000005
#define debug false
#define ll long long
#define lowbit(x) ((-x)&x)
#define ffor(i,d,u) for(int i=d;i<=u;++i)
#define _ffor(i,u,d) for(int i=u;i>=d;--i)
#define mst(array,Num) memset(array,Num,sizeof(array))
const int P = 1e9+7;
int n;
int ans[51],maxnum=-INF;
char c;
int dp[102][102],link[102][102],dp2[102][102];//dp存最大值,dp2存最小值,link为两个点的边
template <typename T>
void read(T& x)
{
    x=0;
    char c;T t=1;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c=='-'){t=-1;c=getchar();}
    do(x*=10)+=(c-'0');while((c=getchar())>='0'&&c<='9');
    x*=t;
}
template <typename T>
void write(T x)
{
    int len=0;char c[21];
    if(x<0)putchar('-'),x*=(-1);
    do{++len;c[len]=(x%10)+'0';}while(x/=10);
    _ffor(i,len,1)putchar(c[i]);
}
void AC()
{
    int j,i,k,z,n1;
    scanf("%d",&n);
    j=0;
    getchar();
    c=getchar();
    if(c=='t')link[n][n+1]=0;
    else link[n][n+1]=1;
    scanf("%d",&dp[1][1]);
    dp2[1][1]=dp2[1+n][1+n]=dp[1+n][1+n]=dp[1][1];
    ffor(i,2,n)
    {
        getchar();
        c=getchar();
        if(c=='t')link[i+n-1][i+n]=link[i-1][i]=0;
        else link[i+n-1][i+n]=link[i-1][i]=1;
        scanf("%d",&dp[i][i]);
        dp2[i+n][i+n]=dp2[i][i]=dp[i+n][i+n]=dp[i][i];
    }
    for(i=1;i<=n;++i)
    {
        n1=i+n-1;
        for(j=i;j<=n1;++j)for(k=j+1;k<=n1;++k)dp[j][k]=-INF,dp2[j][k]=INF;
        for(j=1;j<n;++j)
            for(k=i;k<=n1-j;++k)
                for(z=k;z<k+j;++z)
                    if(link[z][z+1]==1)
                    {
                        dp[k][k+j]=max(dp[k][k+j],dp[k][z]*dp[z+1][k+j]);
                        dp[k][k+j]=max(dp[k][k+j],dp2[k][z]*dp2[z+1][k+j]);
                        dp[k][k+j]=max(dp[k][k+j],dp[k][z]*dp2[z+1][k+j]);
                        dp[k][k+j]=max(dp[k][k+j],dp2[k][z]*dp[z+1][k+j]);
                        dp2[k][k+j]=min(dp2[k][k+j],dp2[k][z]*dp[z+1][k+j]);
                        dp2[k][k+j]=min(dp2[k][k+j],dp[k][z]*dp[z+1][k+j]);
                        dp2[k][k+j]=min(dp2[k][k+j],dp[k][z]*dp2[z+1][k+j]);
                        dp2[k][k+j]=min(dp2[k][k+j],dp2[k][z]*dp2[z+1][k+j]);
                    }
                    else
                    {
                        dp[k][k+j]=max(dp[k][k+j],dp[k][z]+dp[z+1][k+j]);
                        dp2[k][k+j]=min(dp2[k][k+j],dp2[k][z]+dp2[z+1][k+j]);
                    }
        ans[i]=dp[i][n1];
        maxnum=max(maxnum,ans[i]);
    }
    printf("%d\n",maxnum);
    for(i=1;i<n;++i)if(ans[i]==maxnum)printf("%d ",i);
    if(ans[n]==maxnum)printf("%d",n);
}
int main()
{
    AC();
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值