沙子合并、石子合并、能量项链解题报告。

Task1 沙子合并:  设有N堆沙子排成一排,其编号为1,2,3,…,N(N< =300)。每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆,每次只能合并相邻的两堆,合并的代价为这两堆沙子的数量之和,合并后与这两堆沙子相邻的沙子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同,如有4堆沙子分别为  1    3    5    2  我们可以先合并1、2堆,代价为4,得到4  5  2  又合并  1,2堆,代价为9,得到9  2  ,再合并得到11,总代价为4+9+11=24,如果第二步是先合并2,3堆,则代价为7,得到4  7,最后一次合并代价为11,总代价为4+7+11=22;问题是:找出一种合理的方法,使总的代价最小。输出最小代价。


   读完题首先想到最简单的暴力搜索,然而数据范围N<=300,这样做一定会TLE。于是我们想到用区间DP来做这道题。用sum【j】表示前j个数之和。

可得到状态转移方程:f【i】【j】=min(f【i】【l】+f【l】【j】+sum【j】-sum【i-1】,f【i】【j】)


Task2:石子合并

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分。

这道题与Task1的区别只有是放在了环形操场上,所以我们可以把环形问题“”完全“”转换成长度为2倍原长的线性结构。如ABC环形结构转换成ABCABC的线性结构。

注意,在解题时我们应把它完全当做是一个线性结构,只在输出的时候有所区别。再注意:循环顺序。

可得到状态转移方程:a[i][j]=max(a[i][j],a[i][l]+a[l+1][j]+sum[j]-sum[i-1]);            b[i][j]=min(b[i][j],b[i][l]+b[l+1][j]+sum[j]-sum[i-1]);   


Task3:能量项链(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。

这道题和石子合并极为相似,整体思路差不多,只要细心找出状态转移方程即可AC。

 a[i][j]=max(a[i][j],a[i][l]+a[l+1][j]+name[i]*name[l+1]*name[j+1]);   name【n】表示第n位上的数。


另附task2代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN=200;
int a[MAXN*2][MAXN*2],b[MAXN*2][MAXN*2],sum[MAXN*2],n;

int main(){
    int x;
    memset(a,0,sizeof(a));
    memset(b,127/3,sizeof(b));
    scanf("%d",&n);
    for(int i=1;i<=2*n;i++)
    {
        b[i][i]=0;
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        sum[i]=sum[i-1]+x;
    }
    for(int i=n+1;i<=2*n;i++){
        sum[i]=sum[n]+sum[i-n];
    }
    for(int i=2*n-1;i>=1;i--)
        for(int j=i+1;j<=2*n;j++)
            for(int l=i;l<j;l++){
                a[i][j]=max(a[i][j],a[i][l]+a[l+1][j]+sum[j]-sum[i-1]);
                b[i][j]=min(b[i][j],b[i][l]+b[l+1][j]+sum[j]-sum[i-1]);   
                }
    int ans=0,ss=0x7fffffff;
    for(int i=1;i<=n+1;i++)
    {    int j=i+n-1;
        ans=max(ans,a[i][j]);
        ss=min(ss,b[i][j]);
    }
    printf("%d\n",ss);
    printf("%d\n",ans);
    return 0;
}   


task3代码:

#include <bits/stdc++.h>
using namespace std;
const int N=200;
int a[N*2][N*2],name[N*2];
int main(){    int n;
    memset(a,0,sizeof(a));
    cin>>n;
    for(int i=1;i<=n;i++)cin>>name[i];
    for(int i=n+1;i<=2*n;i++)name[i]=name[i-n];
    for(int i=2*n-1;i>=1;i--)
        for(int j=i+1;j<=2*n;j++)
            for(int l=i;l<j;l++)
            {
            a[i][j]=max(a[i][j],a[i][l]+a[l+1][j]+name[i]*name[l+1]*name[j+1]);   
            }
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        int j=i+n-1;
        maxx=max(a[i][j],maxx);
    }
    cout<<maxx;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值