序列最大和分割、最大距离、滑动窗口最大值问题

数字最大和分割问题

      给出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

解法一、

   遍历数组,将数组的每个元素和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;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值