最大子序列和问题:
#include <iostream>
using namespace std;
#define BUFFER_SIZE 100
/************************************************************************/
/*
问题描述:
给定整数A1,A2...AN(可能有负数),求从k = i 到k = j的Ak求和的最大值。
例如:输入 -2,11, -4,13,-5,-2时,答案为20(从A2到A4)。
*/
/************************************************************************/
//========================================================================
//时间复杂度为o(n3)
//=========================================================================
int MaxSubsequenceSum1(const int a[], int n)
{
int max, ThisSum, i, j, k;
max = 0;
for(i=0; i<n; ++i)
for(j=i; j<n; ++j)
{
ThisSum = 0;
for(k=i; k<j; ++k)
{
ThisSum += a[k];
if(ThisSum > max)
max = ThisSum;
}
}
return max;
}
//=========================================================================
//时间复杂度为o(n2)
//=========================================================================
int MaxSubsequenceSum2(const int a[], int n)
{
int max, ThisSum, i, j;
max = 0;
for(i=0; i<n; ++i)
{
ThisSum = 0;
for(j=i; j<n; ++j)
{
ThisSum += a[j];
if(ThisSum > max)
max = ThisSum;
}
}
return max;
}
//=========================================================================
//时间复杂度为o(nlogn)
/*
最大序列和可能出现在三处地方:整个出现在输入数据的做部分;或者整个出现在右部分;或者跨越输入数据的中部分从而占据左右两部分。
前两两种情况可以递归求解。第三种情况和最大和可以通过求出半部分的最大和(包含前半部分的最后一个元素)以及后半部分最大和(包含后半部分第一个元素)而得到。然后将这两个和加在一起。
*/
//=========================================================================
int MaxSubsequenceSum3(const int a[], int left, int right)
{
int max3(int x,int y,int z);
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBorderSum, RightBorderSum;
int Center, i;
if(left == right) //Base case;
{
if(a[left] > 0)
return a[left];
else
return 0;
}
Center = (left + right) / 2;
//=============================================================
//求不带边界的左、右部分最大子序列和,递归。
//=============================================================
MaxLeftSum = MaxSubsequenceSum3(a, left, Center);
MaxRightSum = MaxSubsequenceSum3(a, Center + 1, right);
//=============================================================
//求左边带边界的最大和
//=============================================================
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for(i=Center; i>=left; --i)
{
LeftBorderSum += a[i];
if(LeftBorderSum > MaxLeftBorderSum)
{
MaxLeftBorderSum = LeftBorderSum;
}
}
//=============================================================
//求右边带边界的最大和
//=============================================================
MaxRightBorderSum = 0; RightBorderSum = 0;
for(i=Center+1; i<=right; ++i)
{
RightBorderSum += a[i];
if(RightBorderSum > MaxRightBorderSum)
{
MaxRightBorderSum = RightBorderSum;
}
}
return max3(MaxLeftSum, MaxRightSum,
MaxLeftBorderSum + MaxRightBorderSum);
}
//求三个数中的最大值
int max3(int x, int y, int z)
{
int max = x > y ? x : y;
max = max > z ? max : z;
return max;
}
//=========================================================================
//时间复杂度为o(n)
//=========================================================================
int MaxSubsequenceSum4(int a[], int n)
{
int ThisSum, i, MaxSum;
MaxSum = 0;
ThisSum = 0;
for(i=0; i<n; ++i)
{
ThisSum += a[i];
if(ThisSum > MaxSum)
MaxSum = ThisSum;
else if(ThisSum < 0)
ThisSum = 0;
}
return MaxSum;
}
//=========================================================================
//测试函数
//=========================================================================
int main()
{
int a[BUFFER_SIZE], i, N;
i = 0;
cout << "please input total numbers N = ";
cin >> N;
cout <<endl;
cout<< "please input N numbers :";
while(i < N)
cin >> a[i++];
cout <<endl;
cout << "first function :" << MaxSubsequenceSum1(a, N)<<endl;
cout << "second function :" << MaxSubsequenceSum2(a, N)<<endl;
cout << "third function :" << MaxSubsequenceSum3(a, 0, N-1)<<endl;
cout << "fourth function :" << MaxSubsequenceSum4(a, N)<<endl;
cin >> N;
return 1;
}
看到习题2.13中的求素数问题,突然想到以前想加一个python群,里面的验证问题如下:
10亿以内与987654互质的数的总和是什么?包括10亿。
当时用来最笨的方法,求10亿以内与987654的最大公因数为1的数的总和,也就是循环gcd(1000000000,987654);
现在想到一个比较快的方法:可以先求出987654的素数分解Array[] = {2,3,97,1987},然后对10亿内的数遍历,如果能整除Array中的任意一个,则进入下次循环,否则将当前的值加入总和中。代码如下:
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
//=================================================================================
//大数质因数分解
//=================================================================================
int getSushu(const unsigned long long &n, vector<unsigned long long> &Sushu)
{
unsigned long long ThisNumber = n;
unsigned long long i = 2;
unsigned long long num = 0;
for(;ThisNumber !=1; ++i)
{
if(ThisNumber % i == 0)
{
ThisNumber /= i;
Sushu.push_back(i);
++num;
--i;
}
}
return num;
}
//======================================================================
//求小于10亿且与某个数N互质的数的和,某群入群验证题。。。
//======================================================================
unsigned long long getSum(unsigned long long N)
{
unsigned long long sum = 0;
unsigned long long base = 1000000000;
vector<unsigned long long> Sushu;
int i = 0;
int sushuNum = getSushu(N,Sushu);
bool isHuzhi = false;
int *sushuArray = new int[sushuNum];
//此处之所以转为数组,是因为如果在while循环里面直接用vector的话,速度会慢的吓人
for(i = 0;i < sushuNum;++i)
{
sushuArray[i] = Sushu[i];
}
while(base)
{
isHuzhi = true;
for(i = 0;i < sushuNum;++i)
{
if(!(base % sushuArray[i]))
{
isHuzhi = false;
break;
}
}
isHuzhi ? sum += base : 0;
--base;
}
delete sushuArray;
sushuArray = NULL;
return sum;
}
//======================================================================
//判断一个数是否为素数。。。
//======================================================================
bool isSushu(int n)
{
int i = 2;
for(;i< (int)sqrt((float)n);++i)
{
if(n % i == 0)
{
return false;
}
}
return true;
}
//======================================================================
//测试
int _tmain(int argc, _TCHAR* argv[])
{
unsigned long long N;
int i = 0;
cout <<"请输入要求解的数字: ";
cin >> N;
vector<unsigned long long> Sushu;
getSushu(N,Sushu);
cout <<"素数分解的结果为:";
for(; i<(int)Sushu.size(); ++i)
{
cout << Sushu[i] << " ";
}
cout << endl;
cout <<"求小于10亿且与某个数N互质的数的和:";
cout << getSum(987654) << endl; //正确值为164851253423261119
cin >> i;
return 0;
}
运行时间中的对数
有二分查找、Gcd(欧几里德算法求最大公因数)、幂运算
#include <iostream>
using namespace std;
#define NOT_FOUND -1
//===================================================================
//对分查找,也就是我们经常说的二分法查找
//时间复杂度:logn
//注意事项:
// 1.首位不要忘记
// 2.判断输入数组a[]与数组长度N是否匹配,有可能越界。若输入N = 0,那查个毛啊,或者数组只有100个数,输入了一个102,那也会出错。
// 3.前提:这个数组得是排过序的。
//===================================================================
int BinarySearch(const int a[], int X, int N)
{
int middle, low, high;
low = 0;high = N - 1;
while(low <= high) //此处一定要是<=,要不然就不会判断到边界
{
middle = (high + low) / 2;
if(a[middle] < X)
low = middle + 1; //加1,是因为a[middle]已经判断过了,就不需要再判断
else if(a[middle] > X)
high = middle - 1;
else
return a[middle];
}
return NOT_FOUND;
}
//===================================================================
//欧几里德算法,也就是我们经常说的计算最大公因数Gcd
//时间复杂度:logn
//不用考虑M和N哪个大,如果N大于M,则第一次迭代后他们两个的值就会调换
//===================================================================
unsigned int Gcd(unsigned int M, unsigned int N)
{
unsigned int Rem;
while(N > 0)
{
Rem = M % N;
M = N;
N = Rem;
}
return M;
}
//===================================================================
//幂运算,即求整数X的N次方。
//常用的方法是使用N-1次自乘。下面是使用递归的方法
//===================================================================
long int Pow(long int X,unsigned int N)
{
if(N == 0)
return 1;
if(N == 1)
return X;
if(N % 2)
return Pow(X*X , N/2) * X;
else
return Pow(X*X , N/2);
}
//===================================================================
//测试
//===================================================================
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
cout << "a[7] = "<<BinarySearch(a,7,10)<<endl; //二分查找
int M, N;
M = 50; N = 15;
cout <<"50个15的最大公因数为:"<<Gcd(50,15)<<endl;
cin>>M;
return 0;
}