[题解]vijos & codevs 能量项链

Posted 阿波罗2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[题解]vijos & codevs 能量项链相关的知识,希望对你有一定的参考价值。

描述

在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为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*109),为一个最优聚合顺序所释放的总能量。

样例1

样例输入1

4
2 3 5 10

样例输出1

710

 

限制

1s

来源

NOIP2006第一题

(转自Vijos1312,题目传送门[codevs]&[vijos])


  这道题和加分二叉树有点像,所以立刻想到区间dp。它们俩的差别大概就在一个是树上,还有一个是在环上。处理环就像骑士那样处理,破环成链,特殊处理两端。于是用f[aflag][i][j]来进行dp.

  (PS,下文的区间指的是项链上的一段,[i, j]表示第i个能量珠到第j个能量珠这一段)

  其中aflag为0时表示是在链上,[i, j]区间的能量珠合并出来的最大值。当aflag为0时表示过剖开点,从j到i(区间[j, n][1, i])。

  在链上的比较简单,很容易可以想出方程 f[0][i][j] = max{f[0][i][k] + f[0][k + 1][j] + a[i] * a[k + 1] + a[(j + 1) % n]}(a表示读入的n个数,下标从0开始)

  过剖开点就有三种情况

  1. k在左端,f[1][i][j] = max{f[0][k][i] + f[1][k - 1][j] + a[(i + 1) % n] * a[k] * a[j]}

  2.  

    k在右端,f[1][i][j] = max{f[0][j][k] + f[1][i][k + 1] + a[j] * a[k + 1] * a[(i + 1) % n]}
  3. 刚好是两个在链上的区间(区间[j, n][1, i])合并,smax(f[1][i][j], f[0][0][i] + f[0][j][n - 1] + a[j] * a[0] * a[i + 1])

 最后在f[0][0][n - 1]f[1][i][i + 1]中找找最大值就好了。

Code

  1 /**
  2  * codevs        & vijos.org
  3  * Problem#1154    & 1312
  4  * Accepted        & Accepted
  5  * Time:13ms    & 30ms
  6  * Memory:364k    & 536k
  7  */ 
  8 #include<iostream>
  9 #include<sstream>
 10 #include<cstdio>
 11 #include<cmath>
 12 #include<cstdlib>
 13 #include<cstring>
 14 #include<cctype>
 15 #include<queue>
 16 #include<set>
 17 #include<map>
 18 #include<stack>
 19 #include<vector>
 20 #include<algorithm>
 21 using namespace std;
 22 typedef bool boolean;
 23 #define smin(a, b) (a) = min((a), (b))
 24 #define smax(a, b) (a) = max((a), (b))
 25 template<typename T>
 26 inline void readInteger(T& u){
 27     char x;
 28     int aFlag = 1;
 29     while(!isdigit((x = getchar())) && x != \'-\');
 30     if(x == \'-\'){
 31         aFlag = -1;
 32         x = getchar();
 33     }
 34     for(u = x - \'0\'; isdigit((x = getchar())); u = u * 10 + x - \'0\');
 35     ungetc(x, stdin);
 36     u *= aFlag;
 37 }
 38 
 39 template<typename T>class Matrix{
 40     public:
 41         T *p;
 42         int lines;
 43         int rows;
 44         Matrix():p(NULL){    }
 45         Matrix(int rows, int lines):lines(lines), rows(rows){
 46             p = new T[(lines * rows)];
 47         }
 48         T* operator [](int pos){
 49             return (p + pos * lines);
 50         }
 51 };
 52 #define matset(m, i, s) memset((m).p, (i), (s) * (m).lines * (m).rows)
 53 
 54 #define idx(i)    a[(i + n) % n]
 55 
 56 int n;
 57 int *a;
 58 Matrix<int> f[2];
 59 
 60 inline void init(){
 61     readInteger(n);
 62     f[0] = Matrix<int>(n, n);
 63     f[1] = Matrix<int>(n, n);
 64     a = new int[(const int)(n)];
 65     for(int i = 0; i < n; i++)
 66         readInteger(a[i]);
 67 }
 68 
 69 inline void solve(){
 70     matset(f[0], 0xf0, sizeof(int));
 71     matset(f[1], 0xf0, sizeof(int));
 72     for(int i = 0; i < n - 1; i++){
 73         f[0][i][i + 1] = a[i] * a[i + 1] * idx(i + 2);
 74     }
 75     for(int i = 0; i < n; i++)    f[0][i][i] = 0;
 76     f[1][0][n - 1] = a[n - 1] * a[0] * a[1];
 77     for(int s = 2; s < n; s++){
 78         for(int i = 0; i + s < n; i++){
 79             int j = i + s;
 80             for(int k = i; k < j; k++){
 81                 smax(f[0][i][j], f[0][i][k] + f[0][k + 1][j] + a[i] * a[k + 1] * idx(j + 1));
 82             }
 83         }
 84         for(int i = 0; i - s < 0; i++){
 85             int j = (i - s + n) % n;
 86             for(int k = i; k > 0; k--){
 87                 smax(f[1][i][j], f[0][k][i] + f[1][k - 1][j] + idx(i + 1) * a[k] * a[j]);
 88             }
 89             for(int k = j; k < n - 1; k++){
 90                 smax(f[1][i][j], f[0][j][k] + f[1][i][k + 1] + a[j] * a[k + 1] * idx(i + 1));
 91             }
 92             smax(f[1][i][j], f[0][0][i] + f[0][j][n - 1] + a[j] * a[0] * a[i + 1]);
 93         }
 94     }
 95     int result = f[0][0][n - 1];
 96     for(int i = 0; i < n - 1; i++){
 97         smax(result, f[1][i][i + 1]);
 98     }
 99     cout << result;
100 }
101 
102 ///Main Funtion
103 int main(int argc, char* argv[]){
104     init();
105     solve();
106     return 0;
107 }

以上是关于[题解]vijos & codevs 能量项链的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 车站分级 (luogu 1983 & codevs 3294 & vijos 1851) - 拓扑排序 - bitset

2017.3.11[codevs1937]NOI2010能量采集

Vijos P1312 能量项链

codevs1154 能量项链

codevs 1154 能量项链

1154 能量项链 2006年NOIP全国联赛提高组 codevs