数组分段和最大值最小问题

1. 题目:给定一个数组,和一个值k,数组分成k段。要求这k段子段和最大值最小。求出这个值。

2.分析:这道题目很经典,也很难,个人认为很难。文章中给出了三种算法:算法1,暴力搜索。本题暴力搜索算法并不是很明显,可以使用递归实现暴力搜索。递归首先要有递归式:

                                            n-1
M[n, k] = min { max { M[j, k-1], Ai } }
                j=1                            i=j

n表示数组长度,k表示数组分成几段。初始化条件:

M[1, k] = A0
          n-1
M[n, 1] =  Ai
i=0
很容易发现上述的递归算法拥有指数时间的复杂度,并且会重复计算一些M值。这类的算法一般可以使用动态规划进行优化。使用数组保存一些已经计算得到
的值,采用从低向上进行计算。这就是算法2。文章中还给出了第三种很牛的算法,我是想不到的。这就是使用二分查找应用到这个题目。大牛真是太牛了!! 下面是代码:
View Code
  1 #include <iostream>
  2 #include <cassert>
  3 
  4 using namespace std;
  5 int sum(int A[], int from, int to) { 
  6     int total = 0; 
  7     for (int i = from; i <= to; i++) 
  8         total += A[i]; 
  9     return total; 
 10 } 
 11 //递归的暴力搜素算法
 12 //指数时间的复杂度
 13 int partition(int A[], int n, int k) { 
 14     if (k == 1) 
 15         return sum(A, 0, n-1); 
 16     if (n == 1) 
 17         return A[0]; 
 18 
 19     int best = INT_MAX; 
 20     for (int j = 1; j <= n; j++) 
 21         best = min(best, max(partition(A, j, k-1), sum(A, j, n-1))); 
 22 
 23     return best; 
 24 }
 25 
 26 //改进的动态规划算法
 27 //时间复杂度:O(kN2)
 28 //空间复杂度:O(kN) 
 29 const int MAX_N = 100; 
 30 int findMax(int A[], int n, int k) { 
 31     int M[MAX_N+1][MAX_N+1] = {0}; 
 32     int cum[MAX_N+1] = {0}; 
 33     for (int i = 1; i <= n; i++) 
 34         cum[i] = cum[i-1] + A[i-1]; 
 35 
 36     for (int i = 1; i <= n; i++) 
 37         M[i][1] = cum[i]; 
 38     for (int i = 1; i <= k; i++) 
 39         M[1][i] = A[0]; 
 40 
 41     for (int i = 2; i <= k; i++) { 
 42         for (int j = 2; j <= n; j++) { 
 43             int best = INT_MAX; 
 44             for (int p = 1; p <= j; p++) { 
 45                 best = min(best, max(M[p][i-1], cum[j]-cum[p])); 
 46             } 
 47             M[j][i] = best; 
 48         } 
 49     } 
 50     return M[n][k]; 
 51 }
 52 
 53 
 54 int getMax(int A[], int n) { 
 55     int max = INT_MIN; 
 56     for (int i = 0; i < n; i++) { 
 57         if (A[i] > max) max = A[i]; 
 58     } 
 59     return max; 
 60 } 
 61 
 62 int getSum(int A[], int n) { 
 63     int total = 0; 
 64     for (int i = 0; i < n; i++) 
 65         total += A[i]; 
 66     return total; 
 67 } 
 68 
 69 int getRequiredPainters(int A[], int n, int maxLengthPerPainter) { 
 70     int total = 0, numPainters = 1; 
 71     for (int i = 0; i < n; i++) { 
 72         total += A[i]; 
 73         if (total > maxLengthPerPainter) { 
 74             total = A[i]; 
 75             numPainters++; 
 76         } 
 77     } 
 78     return numPainters; 
 79 } 
 80 
 81 
 82 //想不到的二分查找算法
 83 //时间复杂度:O(N log ( ∑ Ai )).
 84 //空间复杂度:0(1)
 85 int BinarySearch(int A[], int n, int k) { 
 86     int lo = getMax(A, n); 
 87     int hi = getSum(A, n); 
 88 
 89     while (lo < hi) { 
 90         int mid = lo + (hi-lo)/2; 
 91         int requiredPainters = getRequiredPainters(A, n, mid); 
 92         if (requiredPainters <= k) 
 93             hi = mid; 
 94         else
 95             lo = mid+1; 
 96     } 
 97     return lo; 
 98 }
 99 int main()
100 {
101     enum{length=9};
102     int k=3;
103     int a[length]={9,4,5,12,3,5,8,11,0};
104     cout<<partition(a,length,k)<<endl;
105     cout<<findMax(a,length,k)<<endl;
106     cout<<BinarySearch(a,length,k)<<endl;
107     return 0;
108 }
 
 

参考文章:

http://www.leetcode.com/2011/04/the-painters-partition-problem-part-ii.html

http://www.leetcode.com/2011/04/the-painters-partition-problem.html

 

转载于:https://www.cnblogs.com/ZJUKasuosuo/archive/2012/08/16/2641892.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值