POJ 1179 Polygon(最优矩阵链乘)

此题意思很清楚,通过不停的把2个点通过计算变成一个点。顺序自选,问你得出来最大的值是多少,是最优矩阵链乘的升级版。

需要枚举断开的边,然后下面再和最优矩阵链乘一样的递推公式dp[i][j] = min{dp[i][k]乘或加dp[k+1][j]},但是。怎么写是错的。。

我也是犯了这个错,为什么是错,加法肯定是对的,但是乘法就不一样了,因为最大值可能是由2个最小值乘起来得到的,所以必须要用2个DP来分别保存最大值和最小值。最小值得到有2种方式,如果全是正数或者全是负数,那么就是2个最小的得到,如果有正有负,则是左边最小乘右边最大或者是左边最大乘右边最小。

还要注意的一点是因为断开的地方不确定,而每个点的标号都是确定的,所以就会有从最后一个到第一个这种情况,要用取模,具体怎么做可以看代码,或者自己想。

还有一点。我第一次AC的时候用的是每一次确定一个新的断开的边的时候,重新从头开始推,后来我改成如果dp[i][j]的数字不等于初始值-1了,那么就不推这个了,也AC了。本来我觉得不太对,后来我仔细想了想,因为是顺时针推的,所以假设断开一条边后,边2头的数字顺时针分别是i和j,算出dp[i][j],换一条边后,如果还要求i和j的dp值,那么只可能是dp[j][i]了,故是不会出现最优解遗漏的情况的。而且还可以节约很多中间多余推的时间。

AC代码:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<ctime>
using namespace std;
#define NMAX 100005
#define ll long long
int a[100];
int dmax[100][100],record[100],dmin[100][100];

int main()
{
    int n,i,j,d,k;
    while(~scanf("%d\n",&n))
    {
        memset(dmax,-1,sizeof(dmax));
        for(i = 0; i < n; i++)
        {
            char temp;
            scanf("%c %d ",&temp,&dmax[i][i]);
            dmin[i][i] = dmax[i][i];
            if(temp == 't') a[i] = 1;
            else a[i] = 0;
        }
        int ans1,ans2,tt=-NMAX,cnt;
        for(d = 0; d < n; d++)
        {
            for(i = 1; i < n; i++)
                for(j = d; j+i < d+n; j++)
                {
                    ans1 = -NMAX;ans2 = NMAX;
                    if(dmax[j%n][(j+i)%n] != -1) continue;
                    for(k = j; k < j+i; k++)
                    {
                        if(a[(k+1)%n])
                        {
                            ans1 = max(ans1,dmax[j%n][k%n]+dmax[(k+1)%n][(j+i)%n]);
                            ans2 = min(ans2,dmin[j%n][k%n]+dmin[(k+1)%n][(j+i)%n]);
                        }
                        else
                        {
                            ans1 = max(ans1,dmax[j%n][k%n]*dmax[(k+1)%n][(j+i)%n]);
                            ans1 = max(ans1,dmin[j%n][k%n]*dmin[(k+1)%n][(j+i)%n]);
                            ans2 = min(ans2,dmin[j%n][k%n]*dmin[(k+1)%n][(j+i)%n]);
                            ans2 = min(ans2,dmax[j%n][k%n]*dmin[(k+1)%n][(j+i)%n]);
                            ans2 = min(ans2,dmin[j%n][k%n]*dmax[(k+1)%n][(j+i)%n]);
                        }
                    }
                    dmax[j%n][(j+i)%n] = ans1;
                    dmin[j%n][(j+i)%n] = ans2;
                }
            if(ans1>=tt)
            {
                if(ans1 == tt&&cnt)
                    record[cnt++] = d+1;
                else
                {
                    tt = ans1;
                    cnt = 0;
                    record[cnt++] = d+1;
                }
            }
        }
        printf("%d\n",tt);
        for(i = 0; i < cnt; i++)
            printf("%d ",record[i]);
        printf("\n");
    }
    return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值