[题解]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。
格式
限制
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开始)
过剖开点就有三种情况
-
k在左端,f[1][i][j] = max{f[0][k][i] + f[1][k - 1][j] + a[(i + 1) % n] * a[k] * a[j]}
-
- 刚好是两个在链上的区间(区间[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