[动态规划] 洛谷P1063 能量项链 (石子合并)

来源:互联网 发布:战天堂翅膀进阶数据 编辑:程序博客网 时间:2024/06/06 11:36

洛谷P1063 能量项链

题目很长啊,大概意思就是这样

能量项链上有N颗能量珠。能量珠有一个头标记和一个尾标记(即x,y)并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。
如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。
把一串的珠子两两合并(必须要是相邻的,且按顺序),使一串项链释放出的总能量最大
求最大能量

例如:设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。

注意!项链是来得。题目也好心给出了
至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序


如果我告诉你这是DP,是不是有点蒙?

不急不急,我们先来看看这道题变形前的样子


石子合并

(不好意思我怎么都找不到好的来源)

题意:

在操场上沿一直线排列着 n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆, 并将新的一堆石子数记为该次合并的得分。

计算将n堆石子合并成一堆的最小得分。

【输入格式】

第1行是石子堆数n≤100;

以下n行,每行一个整数,为各堆石子数(≤10000)。
【输出格式】
输出合并的最小得分。

样例输入
7
13 7 8 16 21 4 18

样例输出
239

题意简单了很多啊。
PS:分数的计算方式:每一次合并时,分数+=这两个石子的和
合并得到的这个石子的值为原来的两个石子的和

这道题应该是背包来得。

#include<cstdio>  #include<cstring>  using namespace std;  int n,f[100][100],a[100],sum[100][100];  int mymin(int x,int y) {return x>y?y:x;}//min int main() {      scanf("%d",&n);      for(int i=1;i<=n;i++)scanf("%d",&a[i]);      memset(f,63,sizeof(f));  //将f数组全部设为int的最大值    for(int i=1;i<=n;i++)     {          f[i][i]=0;//因为自己和自己不能合并,为0        sum[i][i]=a[i];  //sum[i][j]:第i个到第j个石子的和        for(int j=i+1;j<=n;j++)sum[i][j]=sum[i][j-1]+a[j];  //累加        //f[i][j]:合并i~j个石子时的最优情况    }      for(int x=2;x<=n;x++)     //第一层,x个石子依次合并的情况    {          for(int i=1;i<=n-x+1;i++)         {               int j=i+x-1;               //解释i和j:就像一个箱子上有一个可以左右移动的开口,能看到从i到j的连续的x个石子(能看到i这个点但是看不到j),i:最左端,j:最右端 。但i,j都不能超过整个箱子的左右端点            for(int k=i;k<=j-1;k++)             //解释k:我们在i~j这一段取一个点k,这样就分成了i~k和k+1~j两部分。分别计算出两边的最小值,就可以得出i~j的最小值            {                    f[i][j]=mymin(f[i][j],f[i][k]+f[k+1][j]+sum[i][j]);    //分数计算            }          }      }      printf("%d\n",f[1][n]); //输出合并1~n的情况 } 

让我们回到能量项链这道题
能量项链相比石子合并是不是只是一个变形?
每个石子由1个值改为两个值
线改成环
最小值改成最大值不就是一个min和max的区别吗
应该没什么太大不同了。。。

#include <cstdio> #include <cstring> #include <iostream> using namespace std; int f[210][210]; struct node {     int x,y; //头标记和尾标记}a[210]; int main() {     int n;     scanf("%d",&n);     for (int i=1;i<=n;i++)     {         scanf("%d",&a[i].x);         a[i-1].y=a[i].x; //前一个珠子的尾标记等于后一个珠子的头标记    }     a[n].y=a[1].x; //注意是环!收尾相连    for (int i=1;i<n;i++)         a[i+n]=a[i]; //复制一段在后边,那么从i~i+n就是看成是一个从i开始的一条线了,省了一条循环    memset(f,0,sizeof(f)); //赋0    //f[i][j]:合并第i~j个珠子时的最优情况    for (int k=1;k<=n;k++) //枚举长度(与合并石子的x相似)    {         for (int i=1;i<2*n-k;i++) //从第i个石子开始断,,那么从i~i+n就是一条线        {             for (int j=i;j<i+k;j++) //在i~i+k中选一个点j分成i~j和j+1~i+k两段(与合并石子的k相同)            {                 f[i][i+k]=max(f[i][i+k],f[i][j]+f[j+1][i+k]+a[i].x*a[j].y*a[i+k].y); //计算能量            }         }     }     int ans=0;     for (int i=1;i<=n;i++)     {         ans=max(f[i][i+n-1],ans);         //扫一遍,看看从哪里断得到的值最大    }     printf("%d",ans);     return 0; } 
0 0