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代码:
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;
}