区间DP
-
和石子合并类似。新增了乘法运算符。
-
思路:
-
拆环为链
-
t -7 t 4 x 2 x 5 ---> t -7 t 4 x 2 x 5 t -7 t 4 x 2 x 5
-
这么干的好处是,不用考虑圆环的首尾相接处的特殊处理。
-
-
状态设计
- (F[L][L+N])就是拆除了边(L)后的最佳结果。
- 对于乘法来说,有的规则似乎不适用。例如 负负得正。但是,经过一些简单的数学推导可以得出,最大值可能来自于两个小负数相乘,或者两个大证书相乘。
- 进一步归纳,就是 同号两数相乘,且两数绝对值较大 。
- (FMax]][])和(FMin[][])分别维护绝对值最大的正数和负数。
-
状态转移
- 对于加法,按照普遍的经验即可。
- 重点是乘法,牢记同号得正,异号得负。
-
-
参考代码及注释
#include <stdio.h> #include <string.h> #define Clean(X,K) memset(X,K,sizeof(X)) #define GC getchar() #define Min(X,Y) (X<Y?X:Y) #define Max(X,Y) (X>Y?X:Y) int Qread () { int X = 0 , F = 1; char C = GC ; while (C > '9' || C < '0') { if (C == '-') F = -1 ; C = GC ; } while (C >='0' && C <='9') { X = X * 10 + C - '0' ; C = GC ; } return X *F ; } const int Maxn = 52 , INF = 20021020; int N , A[Maxn << 1] , F_Max[Maxn << 1][Maxn << 1] , F_Min[Maxn << 1][Maxn << 1]; char O[Maxn << 1]; /* 变量名解释 A[]:原始数据 O[]:原始运算符 F_Max[Maxn << 1][Maxn << 1] , F_Min[Maxn << 1][Maxn << 1]:分别维护绝对值最大的正数和负数 */ int main () { Clean (F_Max , ~0x3f) , Clean (F_Min , 0x3f) ; // freopen ("Polygon.txt" , "r" , stdin) ; N = Qread () ; for (int i = 1 ; i <= N; ++ i) { do { O[i] = O[i + N] = GC ; } while (O[i] != 't' && O[i] != 'x'); A[i] = A[i + N] = F_Max[i][i] = F_Min[i][i] = F_Max[i + N][i + N] = F_Min[i + N][i + N] = Qread () ; } for (int Len = 2 ; Len <= N ; ++ Len) { for (int St = 1 ; St <= (N << 1) - Len; ++ St) { for (int K = St ; K < St + Len - 1 ; ++ K) { /* Len : 枚举区间的长度 St :区间的左端点 K :把区间分成 [St,K]和[K + 1,St + Len - 1]两部分 */ if (O[K + 1] == 't') { F_Max[St][St + Len - 1] = Max (F_Max[St][St + Len - 1] , F_Max[St][K] + F_Max[K + 1][St + Len - 1]) ; F_Min[St][St + Len - 1] = Min (F_Min[St][St + Len - 1] , F_Min[St][K] + F_Min[K + 1][St + Len - 1]) ; } else { F_Max[St][St + Len - 1] = Max (F_Max[St][St + Len - 1] , F_Min[St][K] * F_Min[K + 1][St + Len - 1]) ; F_Max[St][St + Len - 1] = Max (F_Max[St][St + Len - 1] , F_Max[St][K] * F_Max[K + 1][St + Len - 1]) ; //负负得正,取得较大值 F_Min[St][St + Len - 1] = Min (F_Min[St][St + Len - 1] , F_Max[St][K] * F_Min[K + 1][St + Len - 1]) ; F_Min[St][St + Len - 1] = Min (F_Min[St][St + Len - 1] , F_Min[St][K] * F_Max[K + 1][St + Len - 1]) ; //正负得负,取得较小值 } } } } int Ans = -INF ; for (int i = 1 ; i <= N; ++ i) Ans = Max (Ans , F_Max[i][i + N - 1]) ; printf ("%d " , Ans) ; for (int i = 1 ; i <= N; ++ i) if (Ans == F_Max[i][i + N - 1]) printf ("%d " , i) ; fclose (stdin) , fclose (stdout) ; return 0 ; }