数字最大和分割问题
给出N个数字,将其分成M份,每一份的和是相等的,求和最大的分割,每
一份并不要求是原序列中连续的元素。
比如3 2 4 3 6,最大和的分割是3 2 4和3 6
假定每一份的和是已知的,为S,判断N个元素的序列是否能被分成M份,
每一份元素的和为S。判断的方法如下,首先从N个元素中任取一个,如
果当前份和不为S,接着再取一个,直到和为S。然后在剩下的元素中继
续这一过程,直到选取完所有的元素,这一过程正是一个全排列的过程。
计算N个数字之和Sum,最大的数Max,S/Max=K, 则S的取值为
Sum/K,Sum/K-1, Sum/K-2,,,2的分割,则该算法的复杂度为K*N!
这个算法有些地方可以考虑优化,一、每一份的元素的顺序不需要考虑,但
是上面给出的全排列就考虑了顺序,二、全排列并不需要计算完就可以
提前终结,比如S为4,先选取了一个元素3,但是序列中没有为1的元素,则
没有必要接着列举其他的排列。代码并没有考虑这两个问题。
#include <vector>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <functional>
bool check( std::vector<int> &vi, int sum ) {
int temp = 0;
for( int i = 0; i < vi.size(); i++ ) {
temp += vi[i];
if( temp > sum ) return false;
if( temp == sum ) temp = 0;
}
return true;
}
bool permPart( std::vector<int> &vi, int sum, int begin) {
if( begin >= vi.size() )
return (check(vi, sum) ? true : false);
for( int i = begin; i < vi.size(); i++ ) {
std::swap( vi[begin], vi[i] );
if( permPart(vi, sum, 1+begin) ) return true;
std::swap( vi[begin], vi[i] );
}
return false;
}
bool findMaxSubarrySum( std::vector<int> &vi, int &max ) {
std::sort( vi.begin(), vi.end(), std::less<int>() );
int big = vi[vi.size() - 1];
int sum = std::accumulate( vi.begin(), vi.end(), 0 );
bool find = false;
for( int k = sum / big; k >= 2; k-- ) {
int cur = sum / k;
if( k * cur == sum && permPart(vi, cur, 0) ) {
max = cur;
find = true;
}
}
return find;
}
测试
int main ( int argc, char *argv[] ) {
int a[] = { 3, 2, 4, 3, 6 };
std::vector<int> vi(a, a + 5);
int max_sum;
if( findMaxSubarrySum(vi, max_sum) ) {
std::cout << "max_sum:" << max_sum << std::endl;
}
int a1[] = {1, 2, 4, 3, 3, 1};
std::vector<int> vi1(a1, a1 + sizeof(a1)/ sizeof(int) );
int max_sum1;
if( findMaxSubarrySum(vi1, max_sum1) ) {
std::cout << "max_sum1:" << max_sum1 << std::endl;
}
getchar();
return 0;
} /* ---------- end of function main ---------- */
最大距离问题
给定一个序列S,S[i]<S[j], 求j-i的最大值
暴力搜索
指针i从头遍历序列,对于每一个i,指针j从末尾向前遍历,找到第一个大于S[i]的数,计算j-i
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
#include <stdlib.h>
using namespace std;
int bruteForce( int b[], int len ) {
int max = 0;
for( int i = 0; i < len; i++ ) {
int j;
for( j = len - 1; i < j && b[i] >= b[j];
j-- ) ;
if( j - i > max ){
max = j - i;
}
}
return max;
}
排序方法
构造一个新的数组,保存val和index比如 2 1 3 4 5
构造的新数组如下
| 2 | 1 | 3 | 4 | 5 |
| 1 | 2 | 3 | 4 | 5 |
以原始数组里面的元素作为关键字排序结果如下:
| 1 | 2 | 3 | 4 | 5 |
| 2 | 1 | 3 | 4 | 5 |
遍历排好序的数组,假如我们遍历到关键字2,其index是1,已经遍历的1
的index是2,显然还没有遍历到的关键字都比2大,还没有遍历到的关键
字的index也是已知的,因为已经遍历过的关键字的index的是已知的。
设计方法如下:设计标志位,表示该index的关键字是否遍历了,标志位
从后向前遍历。index标志位为false,表明该index还没有遍历,并且该
index在正在遍历的关键字的后面,index-index[key]为当前key的最大距
离,并且设置index标志位,移动key的遍历指针。
struct DInd {
int val_;
int index_;
DInd( int val, int index ) : val_(val), index_(index) { }
DInd( ) : val_(0), index_(-1) { }
bool operator<( const DInd & di ) const {
return val_ < di.val_;
}
};
int sortingMethod( int b[], int len ) {
std::vector<DInd> di( len, DInd() );
for( int i = 0; i < len; i++ ){
di[i].val_ = b[i];
di[i].index_ = i;
}
std::sort( di.begin(), di.end(), std::less<DInd>() );
std::vector<bool> m( len, false );
int mi = len - 1;
int max = 0;
for( int i = 0; i < len; i++ ) {
m[di[i].index_] = true;
while( mi >= 0 && m[mi] == true )
mi--;
if( mi - di[i].index_ > max )
max = mi - di[i].index_;
}
return max;
}
测试
int main()
{
int b[] = { 4, 3, 5, 2, 1, 0,6, -1 };
cout<<bruteForce( b, sizeof(b)/sizeof(int) )<<endl;
cout<<sortingMethod(b, sizeof(b)/sizeof(int) )<<endl;
getchar();
return 0;
}
滑动窗口最大值问题
问题描述
给定一个长数组A,有一个大小为W的滑动窗口从最左边移动到最右边,每
次滑动窗口移动一个位置,求解窗口每次滑动的B[i], B[i]是
A[i]到A[i+w-1]的最大值
例如:数组为[1 3 -1 -3 5 3 6 7],w为3
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
次滑动窗口移动一个位置,求解窗口每次滑动的B[i], B[i]是
A[i]到A[i+w-1]的最大值
例如:数组为[1 3 -1 -3 5 3 6 7],w为3
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
解法一、
遍历数组,将数组的每个元素和index压入堆中,判断堆顶元素的index是否在i-w+1和i之间,如果是则显然该元素是当前滑动窗口的最大值,否则移除
堆顶元素,直到堆顶元素在滑动窗口内。
#include "stdafx.h"
#include <iostream>
#include <queue>
#include <utility>
#include <assert.h>
#include "my_algorithm.h"
using namespace std;
typedef pair<int, int> Pair;
void maxSlidingWindow( int A[], int n, int w, int B[] ) {
priority_queue<Pair> queue;
for( int i = 0; i < w; i++ ) {
queue.push( Pair(A[i], i) );
}
B[0] = queue.top().first;
for( int i = w; i < n; i++ ) {
queue.push( Pair(A[i], i) );
Pair p = queue.top();
while( i - w + 1 > p.second ) {
queue.pop();
p = queue.top();
}
B[i - w + 1] = p.first;
}
}
解法二、
使用双端队列,队列中保存的是元素的index,队头保存的是最大元素的index,队尾保存的是最小元素的index,每当遍历到一个新的元素,比较当
前元素和队尾元素的大小,如果比队尾元素大,则不断pop,直到为空或者比
队尾元素小,然后加入当前元素的index到队列中,接着比较对头的index是
否在i-w+1到i之间,如果不在,pop,直到得到最大的符合要求的index即可。
void maxSlidingWindow2( int A[], int len, int w, int B[] ) {
deque<int> Q;
for( int i = 0; i < w; i++ ){
while ( !Q.empty() && A[i] >= A[Q.back()] )
Q.pop_back();
Q.push_back(i);
}
B[0] = A[Q.front()];
for( int i = w; i < len; i++ ) {
while ( !Q.empty() && A[i] > A[Q.back()] )
Q.pop_back();
Q.push_back(i);
while( !Q.empty() && Q.front() < i - w + 1 )
Q.pop_front();
B[i - w + 1] = A[Q.front()];
}
}
void printArray( int B[], int len ) {
for( int i = 0; i < len; i++ ) {
cout<<B[i]<<"\t";
}
cout<<endl;
}
测试
#include "stdafx.h"
#include <iostream>
#include "my_algorithm.h"
int _tmain(int argc, _TCHAR* argv[])
{
int a[] = { 1, 3, -1, -3, 5, 3, 6, 7 };
const int La = sizeof(a) / sizeof(int) ;
int b[ La - 2 ] = {0};
maxSlidingWindow( a, La, 3, b );
printArray( b, La - 2 );
maxSlidingWindow( a, La, 3, b );
printArray( b, La - 2 );
return 0;
}