数组分段和最大值最小问题
1. 题目:给定一个数组,和一个值k,数组分成k段。要求这k段子段和最大值最小。求出这个值。
2.分析:这道题目很经典,也很难,个人认为很难。文章中给出了三种算法:算法1,暴力搜索。本题暴力搜索算法并不是很明显,可以使用递归实现暴力搜索。递归首先要有递归式:
n 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