【NOIP2006提高组】能量项链

【问题描述】

    在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。
    需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。
    例如:设N=4,4颗珠子的头标记与尾标记依次为(2,3) (3,5) (5,10) (10,2)。我们用记号⊕表示两颗珠子的聚合操作,(j⊕k)表示第j,k两颗珠子聚合后所释放的能量。则第4、1两颗珠子聚合后释放的能量为:(4⊕1)=10*2*3=60。
这一串项链可以得到最优值的一个聚合顺序所释放的总能量为((4⊕1)⊕2)⊕3)=10*2*3+10*3*5+10*5*10=710。

【输入格式】

输入文件的第一行是一个正整数N(4≤N≤100),表示项链上珠子的个数。
第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1≤i≤N),当1≤i<N时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。
至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。

【输出格式】

输出只有一行,是一个正整数E(E≤2.1*10^9),为一个最优聚合顺序所释放的总能量。

【输入样例1】

4
2 3 5 10

【输出样例1】

710

解题思路】

用head表示第i颗珠子的头标记,用tail表示尾标记,合并两颗相邻珠子所释放的能量为:energy=head[i]*tail[i]*tail[i+1] 
合并时不一定按输入顺序合并,与石子合并问题类似,第n次合并,可以归结到第n-1次合并,具有明显地动规性质。用f[I,j]表示从第i颗珠子合并到第j颗珠子时产生的最大能量,用k表示最后一次的合并位置,则有: dp[i,j]=max{dp[i,k]+dp[k+1,j]+head[i]*tail[k]*tail[j] , i≤k≤j} 上式中,dp[i,k]表示第i颗到第k颗珠子产生的最大能量,dp[k+1,j]表示合并第k+1颗到第j颗时产生的最大能量,head[i]*tail[k]*tail[j]表示最后一次合并时产生的能量。dp[i,j]的值,分成两个区间,取最大值,是典型的区间型动规。最后一次合并时,产生的能量为什么是head[i]*tail[k]*tail[j]呢?假设有5颗珠子,每颗珠子的能量为10,2,3,5,6,当i=1,j=4,k=2时,如图: 
这里写图片描述 
由图可以看出,合并dp[1,2],dp[3,4]后,还剩下1,3,5三颗珠子(从最上面开始顺时针数),此时1号珠子head[1]=10,tail[1]=3,相当于原图的tail[2];3号珠子tail[3]=6,相当于原图的tail[4]。最后合并dp[1,4]时,相当于合并1,3两颗珠子,产生的能量为最右边图的10*3*6,相当于原图中的head[1]*tail[2]*tail[4],即为上式中的head[i]*tail[k]*tail[j]。 
由于项链是一个环,我们把项链以2*n-1长度,一水平线的形式平铺在桌面上,从左到右逐一扫描,得出最大值。

实现: 
重点就是将整体划分为区间,小区间之间合并获得大区间 
状态转移方程的推导如下 
一、将珠子划分为两个珠子一个区间时,这个区间的能量=左边珠子*右边珠子*右边珠子的下一个珠子 
二、区间包含3个珠子,可以是左边单个珠子的区间+右边两珠子的区间,或者左边两珠子的区间右边+单个珠子的区间 
即,先合并两个珠子的区间,释放能量,加上单个珠子区间的能量(单个珠子没有能量。。) 
Energy=max(两个珠子的区间的能量+单个珠子区间的能量,单个珠子的区间的能量+两个珠子的区间的能量 ) 
三、继续推4个珠子的区间,5个珠子的区间。 
于是可以得到方程:Energy=max(不操作的能量,左区间合并后的能量+右区间合并后的能量+两区间合并产生能量) 
两区间合并后产生的能量=左区间第一个珠子右区间第一个珠子总区间后面的一个珠子

#include<bits/stdc++.h>
using namespace std;
int n,e[300],s[300][300],maxn=-1;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){cin>>e[i];e[i+n]=e[i];}
    //珠子由环拆分为链,重复存储一遍
    for(int i=2;i<2*n;i++){
        for(int j=i-1;i-j<n&&j>=1;j--){//从i开始向前推
            for(int k=j;k<i;k++)//k是项链的左右区间的划分点 
            s[j][i]=max(s[j][i],s[j][k]+s[k+1][i]+e[j]*e[k+1]*e[i+1]);
            //状态转移方程:max(原来能量,左区间能量+右区间能量+合并后生成能量)  
            if(s[j][i]>maxn)maxn=s[j][i];//求最大值 
        }
    } 
    cout<<maxn;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40859951/article/details/81782176
今日推荐