[Solution] [IOI1998]Polygon

区间DP

Link

  • 和石子合并类似。新增了乘法运算符。

  • 思路:

    • 拆环为链

      • 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\n" , 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 ;
    }

Thanks!

猜你喜欢

转载自www.cnblogs.com/bj2002/p/11288853.html