1.子序列最大和问题:

给你一串序列x1,x2,……,xn,求max(i,j){xi+……+xj}。

枚举i,j时间复杂度O(N*N)。

简单dp:

设d[i]表示以i结尾的最大序列和。

则ans=max(d[i])。

d[i] = max(d[i-1]+x[i],x[i]);

O(N*N) -> O(N) .

2.最大子矩阵和问题:

给定二维矩阵m[i][j],求某子矩阵的最大和.

ans = max(sum(x1,y1,x2,y2))。

sum(x1,y1,x2,y2)表示行为x1->x2,列为y1->y2的子矩阵的和。

显然枚举x1,y1,x2,y2复杂度为O(n^4)。

解法:套用一维最大序列和。

枚举x1,x2,可用O(N)的方法得到当最优解行为x1,x2的最大值。

这里只用把x[i]换成sum[x2]-sum[x1]即可。。

参考poj1050.

以上多维子矩阵问题可以类似推广,不过仅仅将算法复杂度降低一个数量级。

3.求某个子矩阵,其和最接近某个值T,最接近指|x-T|的值最小。

参考oj网址:http://cstest.scu.edu.cn/soj/problem.action?id=2691

(华迪实训,完善一下)

  • 记s[i][j] = m[1][j]+……+m[i][j]。
  • 分析一维情况:求以第i个数结尾的最接近某个值T的数,通过set记录sum[i]可以在O(logn)的时间内找出最接近的,顺序扫一遍即可,复杂度O(nlogn)。
  • 枚举行坐标,记为i,k,即为一维情况。
  • 总算法复杂度O(n^3logn)。
  • 该算法适合于m[i][j]为实数的情况。

O(N)求一维情况,假定x[i]均为非负数。

则记d[i]表示以i结尾的不超过T的最大值,l[i]记录其左边界。

t = d[i-1] + x[i] ;

比较t和ans看谁更加接近T。

更新d[i],d[i] = t , l[i] = l[i-1] ;

若t > T ,则 while(d[i]-m[l[i]]>T) l[i]++;

附代码如下:

 

   
   
   
   
  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7.  
  8. using namespace std; 
  9.  
  10. inline bool get(int &t) 
  11.     bool flag = 0 ; 
  12.     char c; 
  13.     while(!isdigit(c = getchar())&&c!='-'if( c == -1 ) break ; 
  14.     if( c == -1 ) return 0 ; 
  15.     if(c=='-') flag = 1 , t = 0 ; 
  16.     else t = c ^ 48; 
  17.     while(isdigit(c = getchar()))    t = (t << 1) + (t << 3) + (c ^ 48) ; 
  18.     if(flag) t = -t ; 
  19.     return 1 ; 
  20.  
  21. const int maxn = 21 ; 
  22. int n , m , d[maxn][maxn*100] , s[maxn][maxn*100] ; 
  23.  
  24. inline int get_max(int a,int b)    {    return a > b ? a : b ;    } 
  25. inline int get_min(int a,int b)    {    return a < b ? a : b ;    } 
  26. inline int get_abs(int a)        {    return a < 0 ? -a : a ;    } 
  27. int main() 
  28.     int sth , i , j , k , t , l , r , key , sum ; 
  29.     set<int> mp; 
  30.     set<int>::iterator ite ; 
  31.     /*for( i = 1 ; i < 10 ; i += 2 ) 
  32.         mp.insert(i); 
  33.     while (get(j)) 
  34.     { 
  35.         printf("%d,%d\n",*lower_bound(mp.begin(),mp.end(),j),*upper_bound(mp.begin(),mp.end(),j)); 
  36.     }*/ 
  37.     for( sth = 1 ; get(n) ; sth++) 
  38.     {  
  39.         get(m);    get(key); 
  40.         memset(s,0,sizeof(s)); 
  41.         memset(d,0,sizeof(d)); 
  42.         for ( i = 1 ; i <= n ; i++) 
  43.         { 
  44.             for( j = 1 ; j <= m ; j++) 
  45.             { 
  46.                 get(k); 
  47.                 s[i][j] = s[i-1][j] + k ; 
  48.             } 
  49.         } 
  50.         int ans = 0x7fffffff ; 
  51.         for ( i = 0 ; i < n ; i++) 
  52.         { 
  53.             for ( k = i+1 ; k <= n ; k++) 
  54.             { 
  55.                 //[i][k]区间 
  56.                 mp.clear(); 
  57.                 t = s[k][1]-s[i][1] ; 
  58.                 mp.insert(t);    mp.insert(0); 
  59.                 if( get_abs(ans-key) > get_abs(t-key) ) ans = t ; 
  60.                 for ( j = 2 ; j <= m ; j++) 
  61.                 { 
  62.                     t += s[k][j]-s[i][j] ; 
  63.                     ite = mp.lower_bound(t-key); 
  64.                     if( ite == mp.end() ) 
  65.                     { 
  66.                         --ite; 
  67.                         l = r = *ite; 
  68.                     } 
  69.                     else 
  70.                     { 
  71.                         r = *ite ;     
  72.                         if( ite == mp.begin() ) 
  73.                             l = 0 ; 
  74.                         else ite--; 
  75.                         l = *ite ; 
  76.                     } 
  77.                     mp.insert(t); 
  78.                     if( get_abs(ans-key) > get_abs(t-l-key) ) ans = t - l ; 
  79.                     if( get_abs(ans-key) > get_abs(t-r-key) ) ans = t - r ; 
  80.                 } 
  81.             } 
  82.         } 
  83.         printf("Case %d:\n%d\n",sth,ans); 
  84.     } 
  85.  
  86. /* 
  87. 22 
  88. 1 9 
  89. 2 1 
  90. */