POJ 1179 Polygon 矩阵链乘 记忆化搜索

题目大意:

多边形游戏,有N个顶点的多边形,3 <= N <= 50 ,多边形有N条边,每个顶点中有一个数字(可正可负),每条边上或者是“+”号,或者是“*”号。边从1到N编号,首先选择一条边移去,然后进行如下操作:

1 选择一条边E和边E连接着的两个顶点V1,V2。

2 用一个新的顶点代替边E和V1、V2,新顶点的值为V1、V2中的值进行边上代表的操作得来(相加或相乘)

当最后只剩一个顶点,没有边时,游戏结束。现在的任务是编程求出最后的顶点能获得的最大值,以及输出取该最大值时,第一步需移去的边,如果有多条符合条件的边,按编号从小到大输出

 

题目分析:

求获得的最大值,是个dp的过程,dp[i][j]=max(dp[i][k](opt操作)dp[k+1][j])表示从i到j能获得最大值,也就是合并k和k+1

考虑到dp[i][j]是一段区间的最大值,每次dp会重复利用到里面的值,所以用记忆化搜索。

其中的最大值,可能由最小值得到,所以要同时保存最小值。

其中每次dfs(i,i+n-1),也就是断开i-1和i,dp一个以i开头的长度为N的链。

 

总结一下:这道题是一个区间dp的题目,并且是个环,所以在dp的时候要注意,处理的时候对于>n的对n进行取余。

核心代码

for(int i=l;i<r;i++)
 {
  q1=dfs(l,i),q2=dfs(i+1,r);
  Max=max(Max,);
  Min=min(Min,); 
 } 
 return dp[l%n][r%n]=make_pair(Max ,Min);

如下标准代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#define N 310 
#define LL long long 
const long long INF = (LL)1<<59; 
inline LL getmax(LL a,LL b)
{
    return a<b?b:a;   
} 
inline LL getmin(LL a,LL b)
{
    return a<b?a:b;    
}
using namespace std;
int n,m;
pair<LL,LL> dp[N][N];
long long  num[N],ans=0;
char G,opt[N];
vector<int>VV;
inline pair<LL,LL> dfs(int l,int r)
{
    if(l==r)
        return make_pair(num[l%n],num[r%n]);
    if(dp[l%n][r%n].first!=INF)
        return dp[l%n][r%n];
    LL Max=-INF,Min=INF,res;
    pair<LL,LL> p1,p2;
    for(int i=l;i<r;i++)
    {
        res=0;
        p1=dfs(l,i),p2=dfs(i+1,r);   
        if(opt[(i+1)%n]=='t')
        {
            res=p1.first+p2.first;
            Max=getmax(Max,res); 
            res=p1.second+p2.second;
            Min=getmin(Min,res);   
        }
        else 
        {
            res=p1.first*p2.first;
            Max=getmax(Max,res);
            res=p1.second*p2.second;
            Max=getmax(res,Max);
            res=p1.first*p2.second;
            Max=getmax(res,Max);
            res=p1.second*p2.first;
            Max=getmax(res,Max);
            res=p1.first*p2.second;
            Min=getmin(Min,res);
            res=p2.first*p1.second;
            Min=getmin(Min,res);
            res=p1.second*p2.second;
            Min=getmin(Min,res);
            res=p1.first*p2.first;
            Min=getmin(Min,res);
        }
    }   
    return dp[l%n][r%n]=make_pair(Max,Min);  
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
           for(int j=0;j<n;j++)
           dp[i][j].first=dp[i][j].second=INF;
        for(int i=0;i<n;i++)
        cin>>opt[i]>>num[i];
        LL ans=-INF;
        for(int i=0;i<n;i++)
        {
            LL tem=dfs(i,i+n-1).first;
            if(tem>ans)
            {
                ans=tem;
                VV.clear();
                VV.push_back(i+1);    
            }       
            else if(tem==ans) 
            {
                VV.push_back(i+1);
            }
        }
        printf("%lld\n",ans);
        for(int i=0;i<(int)VV.size();i++)
        {
            printf("%d%s",VV[i],i==(VV.size()-1)?"\n":" ");    
        } 
    }
   	 while(1);
    return 0;
}   


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值