[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!

posted @ 2019-08-02 15:34  Betulaceae  阅读(142)  评论(0编辑  收藏  举报