草庐IT

最大子段和——用蛮力算法,分治策略,动态规划算法三种求法(C语言)

畅宝不秃头 2023-04-12 原文

目录

一、题目

二、算法求解

1、蛮力算法

伪代码

 算法分析

程序

2、分治策略

伪代码

算法分析

程序

3、动态规划算法

伪代码

算法分析

程序


一、题目

设A=<a1,a2,...,an>是n个整数的序列,称<ai,....,aj>为该序列的连续子序列,其中1<=i<=j<=n,子序列的元素之和称为A的子段和:

例如,A=<-2,11,-4,13,-5,-2>,那么它的子段和如下:

长度为1的子段和有:-2,11,-4,13,-5,-2

长度为2的子段和有:9,7,9,8,-7

长度为3的子段和有:5,20,4,6

长度为4的子段和有:18,15,2

长度为5的子段和有:13,13

长度为6的子段和有:11

其中的最大子段和为:11-4+13=20

则最大子段和问题为:

给定n个整数的序列A=<a1,a2,...,an>,求

二、算法求解

1、蛮力算法

通过枚举A的所有连续子序列并且求和,通过比较找出具有最大和的子序列。

伪代码

//算法 Enumerate
//输入:数组A[1..n]
//输出:sum,first,last   //sum为最大子段和,first与last分别是和的首末位置

  1.      
  2.                
  3.               
  4.      
  5.      
  6.                
  7.                

 算法分析

3个for循环,每次执行O(n)次,循环内部是常数操作,所以该算法的时间复杂度为

程序

//算法3.8 Enumerate
//输入:数组A[1..n]
//输出:sum,first,last

#include<stdio.h>
#include<stdlib.h>
void Enumerate (int a[],int n)
{
	int sum = 0;
	int i,j,k;
	int first,last;
	for(i = 0;i < n; i++){
		for(j = i;j < n;j++){
			int thissum = 0;
			for(k = i;k <= j; k++){
				thissum = thissum + a[k];
			}
			if(thissum > sum){
				sum = thissum;
				first = i;
				last = j;
			}
		}
	}
	printf("数组A的最大连续子段和为A[%d..%d]=%d",first+1,last+1,sum);
}

int main()
{
	int A[6]={-2,11,-4,13,-5,-2};
	int i;
	for(i = 0;i < 6;i++)
	{
		printf("%d ",A[i]);
	}
	printf("\n");
	Enumerate(A,6); 
	return 0;
}

2、分治策略

最邻近点对的算法(参考之前的文章)类似,我们可以在 n/2的位置将 A 划分成A1和 A2前后两半,于是 A 的最大子段和可能是三种情况:

  1. 出现在 A1部分
  2. 出现在 A 部分,
  3. 出现在横跨两边的中间部分

前两种情况恰好对应于两个规模减半的子问题,第三种情况需要特殊处理一下,设原问题的输人是 A [1...n],中间分点 k = n /2,那么前半部分子问题的输人是 A [1...k],后半部分子问题的输人是 A [ k +1,n]。在第三种情况下,设这个最大和是 A [ p .. q ],那么, ,从 A [ p ]到 A [ k ]的元素都在 A1 中,从 A [ k 十1]到 A [ q ]的元素都在 A2 中,我们只需要从 A [ k ]和 A [ k +1]分别向前和向后求和即可,比如以 A [ p ...k ]的计算为例,依次计算 A [ k .. k ], A [ k-1, k ],.., A [1...k],记下其中最大的和 S1 ,即 A [ p ... k ],对右半部可以同样处理,只不过扫描方向相反,得到的 S2 就是 A [ k+1... q ]的元素之和,当两个方向的扫描都完成之后,S1+S2 就是横跨中心的最大和,其边界从p到q。这三种情况都计算完成以后,通过比较就可以确定 A 的最大子段和

伪代码

//MaxSubSum(A,left,right) 【分治算法】
//输入:数组A,left,right分别是A的左右边界,1<=left<=right
//输出:A的最大子段和sum及其子段的前后边界

算法分析

设算法对规模为的输人运行时间是 T ( n ),行3和行4是递归调用,每个子问题是原来问题规模的一半,因此需要2T( n /2)时间,行5和行6的处理需要扫描A的每个元素,总计需要O(n)时间,于是递归方程为:

该方程的解为:

程序

//算法3.9 MaxSubSum(A,left,right) 【分治算法】 
//输入:数组A,left,right分别是A的左右边界,1<=left<=right
//输出:A的最大子段和sum及其子段的前后边界

#include<stdio.h>
#include<stdlib.h>

typedef struct max_info{
	int low;
	int high;
	int sum;
};

struct max_info MaxSubSum(int a[],int left,int right)
{
	struct max_info maxinfo;
	maxinfo.sum = 0;
	int i;
	if(left == right){
		maxinfo.sum = a[left]>0 ? a[left] : 0;
		maxinfo.high = right;
		maxinfo.low = right;
		return maxinfo;
	}
	else{
		
		struct max_info leftinfo;//左侧 
		struct max_info rightinfo;//右侧 
		struct max_info croseinfo;//跨越 
		
		
		int center = (left + right) / 2;
		leftinfo = MaxSubSum(a, left, center);
		rightinfo = MaxSubSum(a, center + 1, right);
		
		
		int s1 = 0;
		int suml = 0;
		for(i = center; i >= left; i--)
		{			
			suml += a[i];
			if(suml > s1) 
			{
				s1 = suml;
				croseinfo.low = i;
			}	
		}
	
		int s2 = 0;
		int sumr = 0;
		for(i = center + 1; i < right; i++)
		{			
			sumr += a[i];
			if(sumr > s2)
			{
				s2 = sumr;
				croseinfo.high = i;	
			}	

		}
		croseinfo.sum = s1 + s2;
		if(croseinfo.sum < leftinfo.sum){
			maxinfo.sum = leftinfo.sum;	
			maxinfo.high = leftinfo.high;
			maxinfo.low = leftinfo.low;
		} 			
		if(croseinfo.sum < rightinfo.sum){
			maxinfo.sum = rightinfo.sum;
			maxinfo.high = rightinfo.high;
			maxinfo.low = rightinfo.low;
		} else{
			maxinfo.sum = croseinfo.sum;
			maxinfo.high = croseinfo.high;
			maxinfo.low = croseinfo.low;
		}
			
	}
	return maxinfo;
}

int main()
{
	struct max_info maxinfo;
	int A[6]={-2,11,-4,13,-5,-2};
	int i;
	for(i = 0;i < 6;i++)
	{
		printf("%d ",A[i]);
	}
	printf("\n");
	maxinfo = MaxSubSum(A,0,5); 
	printf("数组A的最大连续子段和为:A[%d..%d]=%d\n",maxinfo.low+1,maxinfo.high+1,maxinfo.sum);
	return 0;
}

3、动态规划算法

为了得到更高效的算法,我们需要在子问题之间建立一个简单的递推关系,因此定义一个优化函数

 对于数组A=<2,-5,8,11,-3,4,6>,其优化函数为:C[1]=2、C[2]=-3、C[3]=8、C[4]=19、C[5]=16、C[6]=20、C[7]=26

在这种优化函数中,C [ i +1]可以通过 C[ i ] 直接得到,因为如果 A [1...  i +1 ]的子段 A [ k .. i +1]是使得 C [ i +1 ]达到最大和的子段,那么 A [ k ... i ]一定是使得 C [ i ]达到最大和的子段.如若不然,存在一个使得 C[ i ]达到更大和的子段 A [ t ... i ],那么在 A [ t ... i ]后面加上 A[ i+1 ]所得到的子段 A [ t ... i +1]之和将大于 C [ i +1].这与 C [ i 十1]是 A [1.. i +1]以元素 A [ i +1]作为最后元素的最大子段和矛盾.这恰好验证了这样定义的优化函数满足优化原则于是,我们在考虑怎样选择才能使得 C [ i +1]达到最大值时,只要考虑一个问题:是否需要把 C [ i ]加到 A [ i +1上?而这取决于 C [ i ]是否大于0.

那么递推关系为:

     若A[1]>0,否则C[1]=0

根据上面的递推公式,得到C[1],C[2],C[3].....C[n,]恰好枚举了以任何元素为最后元素的所有子段的最大和,我们要找到所求问题的最大子段和,就要对上面n个子段和进行比较,找到其中最大和。

伪代码

//MaxSum(A,n) 【动态规划】
//输入:数组A
//输出:最大子段sum,子段的最后位置c

  1.       
  2.       
  3.       
  4.       
  5.       
  6.                    

算法分析

MaxSum(A,n) 算法只有一个for循环,执行次数为O(n),循环体内都是常数次运算,因此算法复杂度为O(n)

程序

//算法3.10 MaxSum(A,n) 【动态规划】 
//输入:数组A
//输出:最大子段sum,子段的最后位置c

#include<stdio.h>
#include<stdlib.h>

void MaxSum(int A[], int n)
{
	int sum = 0;
	int b = 0;
	int i,c;
	for(i = 0;i < n;i++)
	{
		if(b > 0)
			b= b+A[i];
		else
			b = A[i];
		
		if(b > sum)
		{
			sum = b;
			c = i;
		}
	}
	printf("数组A的最大连续子段和为:%d,且子段最后位置为:%d",sum,c+1);
} 

int main()
{
	int A[7]={2,-5,8,11,-3,4,6};
	int i;
	for(i = 0;i < 7;i++)
	{
		printf("%d ",A[i]);
	}
	printf("\n");
	MaxSum(A,7); 
	return 0;
}

有关最大子段和——用蛮力算法,分治策略,动态规划算法三种求法(C语言)的更多相关文章

  1. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  2. ruby - 在 Ruby 中动态创建数组 - 2

    有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.

  3. ruby - 是否可以将 IRB 提示配置为动态更改? - 2

    我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO

  4. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  5. ruby - 如何更优雅地记下这三种情况? - 2

    是否可以让这段代码更紧凑?我在这里错过了什么吗?ifvaluemax_ratemax_rateelsevalueend 最佳答案 这里有一些完全不同的东西:[min_rate,value,max_rate].sort[1] 关于ruby-如何更优雅地记下这三种情况?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/13309740/

  6. ruby-on-rails - carrierwave:在序列化动态属性上安装 uploader - 2

    首先,我使用的是rails3.1.3和来自master的carrierwavegithub仓库的分支。我使用after_init钩子(Hook)来确定基于属性的字段页面模型实例并为这些字段定义属性访问器将值存储在序列化哈希中(希望它清楚我是什么谈论)。这是我正在做的事情的精简版:classPage省略mount_uploader命令让我可以访问我想要的属性。但是当我安装uploader时出现错误消息说“nil类的未定义新方法”我在源代码中读到有方法read_uploader和扩展模块中的write_uploader。我如何必须覆盖这些来制作mount_uploader命令使用我的“虚拟

  7. ruby - 在 Ruby 中动态生成多维数组 - 2

    我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]

  8. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  9. ruby - 动态方法链? - 2

    如何在对象上调用方法名称的嵌套哈希?例如,给定以下哈希:hash={:a=>{:b=>{:c=>:d}}}我想创建一个方法,给定上面的散列,执行以下操作:object.send(:a).send(:b).send(:c).send(:d)我的想法是我需要从一个未知的关联中获取一个特定的属性(这个方法不知道,但程序员知道)。我希望能够指定一个方法链来以嵌套哈希的形式检索该属性。例如:hash={:manufacturer=>{:addresses=>{:first=>:postal_code}}}car.execute_method_hash(hash)=>90210

  10. ruby - 如何使用 method_missing 动态声明方法? - 2

    我有一个ruby​​程序,我想接受用户创建的方法,并使用该名称创建一个新方法。我试过这个:defmethod_missing(meth,*args,&block)name=meth.to_sclass我收到以下错误:`define_method':interningemptystring(ArgumentError)in'method_missing'有什么想法吗?谢谢。编辑:我以不同的方式让它工作,但我仍然很好奇如何以这种方式做到这一点。这是我的代码:defmethod_missing(meth,*args,&block)Adder.class_evaldodefine_method

随机推荐