算法问题分类---整数和问题系列总结

1.给定正整数N,列出所有总和为N的连续正整数串。

方法1:

可以用两个数small,big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到big的序列的和大于n的话,则右移small即从序列中移除最小的数;如果从small到big的序列的和小于n的话,则右移big,相当于向序列中添加下一个数字,一直到small等于(1+n)/2,保证序列中至少有两个数。

方法2:

设最大长度为x(加起来等于同样值的元素最多,则这些元素从最小的元素里取),则

1+2+...+ x = N  => x(x+1)/2 = N

=>x = sqrt(2N+1/4)-1/2 <= sqrt(2N)

设待求出子序列的长度为len ,则依次从len = x-->1找子序列就行了

设序列开始的位置的元素值为s,则对于长度为len的子序为

s,s+1,....s + x-1

该子序列的和为x*s + x(x-1)/2 = N     => s = N/x - (x-1)/2

若s为整数,则s,s+1,.... s + x-1即为所求的符合条件的子序列。

代码(来自网上):

#include <stdio.h>
#include <math.h>
int main()
{
 int m=0,
  n=0,
  start=0,
  end=0,
  flag=0;
 //m为输入的数,n为正整数序列个数,start为显示的开始正整数,end为显示的结束正整数,
 //flag表示有无符合条件的序列存在,0(默认)没有,1有
 float temp=0.0;
 printf("please input a Integer:");
 scanf("%d",&m);
 printf("\n");
 n=(int) (sqrt((double)m)*sqrt((double)2));
 //求出可能的最大的序列个数
 while(n>=2)
 {
  temp=(float)m/n-(float)(n-1)/2;
  //求开始数
  if(temp==(int) temp)
  {
   //判断是不是正整数,即有没有符合符合条件的序列存在
   for(flag=1,start=(int) temp,end=start+n;start<end;start++)
   {
    //flag标志置1,有符合符合条件的序列存在,得出开始整数start和结束整数end
    printf("%d",start);
    printf(" ");
   }
   printf("\n");
  }
  n--;
 }
 if(flag==0)
  //没有符合符合条件的序列存在,则输出"none"
  printf("none");
}

2.和为S的两个数

输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

思想:因为已经排好序,那么如果存在那么两个数,可能是一个比较大,一个比较小,或者是比较相等的两个数!所以我们设一个n1=min(即a[0]),n2=max(即a[n-1]
)开始,实际是n1 = a[low],low=0开始,n2
= a[high],high=n-1开始,然后n1+n2与S(我们需要的和)进行比较,若>,则n2=a[--high];若<,
n1=a[++low],若==,则使用mul保存乘积,还要保存两个数,因为可能不只一对数,它只要乘积小的!扫描直到low>=high终止!

3.判断一包含n个整数a[]中是否存在i、j、k满足a[i]
+ a[j] = a[k]的时间复杂度最小值是:

A.O(n^2)        B. O(n^2*logn)       C. O(n^3)   D. O(nlogn)

分析:先递增排序(O(nlgn)),再固定每一个元素,按照求和为S的两个数字S的方法求解(n*O(n)=O(n^2))

求和问题总结(leetcode 2Sum, 3Sum, 4Sum, K Sum)

http://blog.csdn.net/doc_sgl/article/details/12462151

曾经在网上见了一个帖子讨论该问题O(nlgn)的方法,可惜现在一直找不到了,大概是利用相反数之类的思想。

4.输入两个整数 n 和 m,从数列1,2,3.......n 中随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来。(0-1背包问题)

分析:

问题其实本质上就是0/1背包问题,对于每一个n,我们采用贪婪策略,先考察是否取n,如果取n,那么子问题就变成了find(n-1,m-n),而如果舍弃n,子问题则为find(n-1,m)。

那么,如何制定解的判定策略?我们知道,递归需要边界条件,而针对背包问题,边界条件只有两种,如果n<1或者m<1,那么便相当于“溢出”,无法combo出m,而另一种可能就是在剩余的n个里恰好满足m==n,即此时背包刚好填充满,输出一组解单元。除此之外,再无其他。为了减小递归深度,当n>m时,因为和等于m的数必小于等于m,所以可以设置n=m.

算法:

1.首先判断,如果n>m,则n中大于m的数不可能参与组合,此时置n=m;

2.将最大的数n加入且n==m,则满足条件,输出;

3.将n分两种情况求解:n没有加入,取n=n-1,m=m,递归;

4.n加入,取n=n-1,m=m-n,递归。

5.结束。

代码(来自网上):

intlength;
/*输入两个整数n 和m,从数列,,.......n 中随意取几个数, 使其和等于m ,要求将其中所有的可能..*/
voidfindCombination(int n,intm,bool *flag)
{
  if(n < 1 || m < 1)
   return;
  if(n > m)//--减少递归次数----
   n = m;
  if(n == m)
 {
   flag[n-1] = 1;
   for(inti=0;i<length;i++)
   {
     if(flag[i] == true)
       printf("%d\t",i+1);
   }
   printf("\n");
   flag[n-1] = 0;
 }
 flag[n-1] = 1;
 findCombination(n-1,m-n,flag);
 flag[n-1] = 0;
 findCombination(n-1,m,flag);
}

intmain()
{
  int n, m;
 printf("n  sum\n");
 scanf("%d%d",&n,&m);
 length = n;
  bool *flag = (bool*)malloc(sizeof(bool)*length);
 findCombination(n,m,flag);
 free(flag);
  return 0;
}

5.子集和问题(回溯法状态空间树)---问题4的推广

求n个正整数构成的一个给定集合S={s1,s2,…sn}的子集,子集的和要等于一个给定的正整数d。例如对于S={1,2,6,8}和d=9来说,该问题有两个解:{1,2,6}和{1,8}。当然,这个问题的某些实例是无解的。

思路:把集合元素按照升序排序,即s1<=s2<=...<=sn

我们可以按照二叉树的形式来构造状态空间树,树的根代表了起点,这时还没有对给定的元素做任何决定。根的左右子节点分别代表在当前所求的子集中包含或者不包含S1,同样的,从第一层的节点想左走表名包含S2,向右走则不包含,以此类推。因此,从根到树的某个第i层节点的路径,指出了该节点所代表的的子集中包含了前i个数字中的哪些数字。

我们把这些数字的和s‘记录在节点中。如果s‘等于d,我们就找到了问题的一个解。如果s‘不等于d,当下面两个等式中的任何一个等式成立,我们就把该节点作为没有希望的节点终止掉。

代码(原创):

#include <iostream>
#include <algorithm>

using namespace std;

int arr[] = {8,4,5,6,9,2,1};  //
//int arr[] = {9,8,7,6,5,4,3,2,1};
//int arr[] = {1,2,3,4,5,6,7,8,9,10};

const int len = sizeof(arr)/sizeof(int);
bool *flag = new bool[len];

bool cmp(int x,int y);
void GetSubSetSum(int n,int s);
int Sum(int n);

int main()
{
	int s = 10;
	sort(arr,arr+len,cmp);//sort by desc

	for(int i = 0;i<len;i++)
		flag[i] = false;
	GetSubSetSum(len-1,s);
	return 0;
}

void GetSubSetSum(int n,int s)
{
	if(s == 0)//有解
	{
		for(int i = 0;i<len;i++)
		{
			if(flag[i] == true)
			{
				cout << arr[i] << " ";
				//flag[i] = false;
			}
		}
		cout << endl;

	}

	if(s<arr[n] || s>Sum(n))//s过小或者过大都无解
		return;

	//状态空间树 分支
	//包含arr[n]
	flag[n] = true;
	GetSubSetSum(n-1,s-arr[n]);
	//不包含arr[n]
	flag[n] = false;
	GetSubSetSum(n-1,s);

}

int Sum(int n)
{
	int s = 0;
	//for(int i = 0;i<n;i++)
	for(int i = 0;i<=n;i++)
		s += arr[i];
	return s;
}

bool cmp(int x,int y)
{
	return y < x;
}

6.子数组的最大和

当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。

如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零;不然的话这个负数将会减少接下来的和;

当前和大于最大和,则重置最大和;

输入有一类特殊情况需要特殊处理。当输入数组中所有整数都是负数时,子数组和的最大值就是数组中的最大元素。

记录重置当前和时的数组下标和最后一次重置最大和的数组下标,可以输出该子数组的每一个元素。

//子数组的最大和[算法]
void MaxSum(int array[], unsigned int len)
{
	int beg = 0,last = 0;//开始求和  和 最后一次更新最大值的位置
	if(NULL == array || len <=0){
		return;
	}

	int curSum = 0, maxSum = 0;
	int i = 0;
	for(i=0; i<len; i++){
		curSum += array[i];		// 累加

		if(curSum < 0){			//-key2-- 当前和小于0,重置为0
			beg = i + 1;//
			curSum = 0;
		}

		if(curSum > maxSum){	// -key1--当前和大于最大和,则重置最大和-- (3, 10, -4, 7, 2, -5,4,2)
			maxSum = curSum;
			last = i;//
		}
	}

	/*输入有一类特殊情况需要特殊处理。当输入数组中所有整数都是负数时,子数组和的最大值就是数组中的最大元素。*/
	if(maxSum == 0){			// -key3--最大和依然为0,说明数组中所有元素都为负值
		maxSum = array[0];
		for(i=1; i<len; i++){
			if(array[i] > maxSum){
				maxSum = array[i];
				beg = i;
				last = i;
			}
		}
	}

	cout << "maxSum:" << maxSum << endl;

	cout << "sub arr is: " << endl;
	for(i = beg;i<=last;i++)
		cout << array[i] << " ";
	cout << endl;
}

7.N个鸡蛋放进M个篮子问题

有N个鸡蛋和M个篮子,把鸡蛋放到M个篮子里,每个篮子都不能为空。另外,需要满足:任意一个小于N的正整数,都能由某几个篮子内蛋的数量相加的和得到。写出程序,使得输入一个(N,M),输出所有可能的分配情况。

时间: 2024-10-08 05:12:58

算法问题分类---整数和问题系列总结的相关文章

算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)

算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification) 0.写在前面的话 我个人一直很喜欢算法一类的东西,在我看来算法是人类智慧的精华,其中蕴含着无与伦比的美感.而每次将学过的算法应用到实际中,并解决了实际问题后,那种快感更是我在其它地方体会不到的. 一直想写关于算法的博文,也曾写过零散的两篇,但也许是相比于工程性文章来说太小众,并没有引起大家的兴趣.最近面临毕业找工作,为了能给自己增加筹码,决定再次复习算法方面的知识,我决定趁这个机会,写一系列关于

转载:算法杂货铺——分类算法之决策树(Decision tree)

作者:张洋 算法杂货铺——分类算法之决策树(Decision tree) 2010-09-19 16:30 by T2噬菌体, 44346 阅读, 29 评论, 收藏, 编辑 3.1.摘要 在前面两篇文章中,分别介绍和讨论了朴素贝叶斯分类与贝叶斯网络两种分类算法.这两种算法都以贝叶斯定理为基础,可以对分类及决策问题进行概率推断.在这一篇文章中,将讨论另一种被广泛使用的分类算法——决策树(decision tree).相比贝叶斯算法,决策树的优势在于构造过程不需要任何领域知识或参数设置,因此在实际

【机器学习基础】机器学习算法的分类——关于如何选择机器学习算法和适用解决的问题

引子 系统的学习机器学习课程让我觉得受益匪浅,有些基础问题的认识我觉得是非常有必要的,比如机器学习算法的类别. 为什么这么说呢?我承认,作为初学者,可能无法在初期对一个学习的对象有全面而清晰的理解和审视,但是,对一些关键概念有一个初步并且较为清晰的认识,有助于让我们把握对问题的认识层次,说白了,就是帮助我们有目的的去学习心得知识,带着问题去学习,充满对解决问题的动力去实验,我觉得这种方式是有益并且良性的. 之前,我遇到过很多这方面的问题,可能出于对问题分析不够,在寻找解决的问题的方法或者模型的时

《机器学习实战》学习笔记:利用Adaboost元算法提高分类性能

一. 关于boosting算法的起源 boost 算法系列的起源来自于PAC Learnability(直译过来称为:PAC 可学习性).这套理论主要研究的是什么时候一个问题是可被学习的. 我们知道,可计算性在计算理论中已经有定义,而可学习性正是PAC Learnability理论所要定义的内容.另外,在计算理论中还有很大一部分精力花在研究问题是可计算的时候,其复杂度又是什么样的.因此,在计算学习理论中,也有研究可学习的问题的复杂度的内容,主要是样本复杂度 (Sample Complexity)

K近邻算法——多分类问题

给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例,这K个实例的多数属于某个类,就把该类输入实例分为这个类. KNN是通过测量不同特征值之间的距离进行分类.它的的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别.K通常是不大于20的整数.KNN算法中,所选择的邻居都是已经正确分类的对象.该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别. 下面通过一个简单的例子说明一下

利用AdaBoost元算法提高分类性能

当做重要决定时,大家可能都会吸取多个专家而不只是一个人的意见.机器学习处理问题时又何尝不是如此?这就是元算法背后的思路.元算法是对其他算法进行组合的一种方式. 自举汇聚法(bootstrap aggregating),也称为bagging方法,是从原始数据集选择S次后得到S个新数据集的一种技术.新数据集和原数据集的大小相等.每个数据集都是通过在原始数据集中随机选择一个样本来进行替换而得到的.在S个数据集建好之后,将某个学习算法分别作用于每个数据集就得到了S个分类器.当我们要对新数据进行分类时,就

大整数算法[01] 大整数的表示和相关定义

★ 相关的数据类型定义 在干正事之前,先定义好各种数据类型还是很有必要的,避免在以后的编码中引起混乱. uintX   X位无符号整形,如uint32表示32位无符号整形 intX    X位有符号整形,如int32表示32位有符号整形 基本数据类型定义: #ifdef _MSC_VER            typedef __int8              int8;            typedef __int16             int16;            typ

第七章:利用AdaBoost元算法提高分类性能

本章内容□ 组合相似的分类器来提髙分类性能□应用AdaBoost算法□ 处理非均衡分类问题 7.1基于数据集多重抽样的分类器

数据挖掘十大经典算法--CART: 分类与回归树

一.决策树的类型  在数据挖掘中,决策树主要有两种类型: 分类树 的输出是样本的类标. 回归树 的输出是一个实数 (比如房子的价格,病人呆在医院的时间等). 术语分类和回归树 (CART) 包括了上述两种决策树, 最先由Breiman 等提出.分类树和回归树有些共同点和不同点-比如处理在何处分裂的问题. 分类回归树(CART,Classification And Regression Tree)也属于一种决策树,之前我们介绍了基于ID3和C4.5算法的决策树. 这里仅仅介绍CART是如何用于分类