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

本文解析洛谷P1063能量项链问题,通过对比石子合并问题,采用动态规划方法,实现求解能量项链的最大能量释放方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷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; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值