一道算法题,看看大家的思路

题目描述:有31,-41,59,26,-53,58,97,-93,-23,84十个数。SUM(N,M)表示从第N个数到到第M个数的和。例如:SUM(2,3)=-41+59=18。问:最大的和是多少?对应的N和M是多少?

  这个题目并不难,实现的方法多种多样。最坏的算法,遍历所有的情况,求出最大和。

  我在这儿提一个算法的思路,不是最优的,主要是讲解这个算法的。

  模拟生物算法(谢谢装配脑袋指正,这应该是遗传算法)。

  根据题目,构造生物S,生物S有三个属性N、M、V。N表示开始的下标,M表示结束的下标,和题目中的定义一样。V表示从第N个数到第M个数的和,V和N、M是相关的。因此,可以用S(N,M)表示这个生物。

  先期构造10个这样的生物。称为第一代。

  生物有两个特性,繁衍性和变异性。

  繁衍性:生物S1(N1,M1)和生物S2(N2,M2)繁衍的后代为生物S3(N1,M2)和生物S4(N2,M1)

  变异性:生物S1(N1,M1)产生变异,得到S2(N2,M1)或者是S2(N1,M2)

  第一代生物通过繁衍和变异得到10个后代(繁衍和变异的比例自定)。这样一共有20个生物。然后这20个生物采用优胜劣汰的方法,保留10个V最大的生物,淘汰10个生物。这称为自然选择的一代。

  模拟大自然的自然选择,通过初期的10个生物,经过5代的自然选择,基本上就能得到最优解。

 

  模拟生物算法(遗传算法),就是利用繁衍和变异以及优胜劣汰,保留最优的生物,得到最优解。在某些实际问题中,能达到不错的效果。不过模拟生物算法(遗传算法)不能保证一定能在最优解收敛,但基本上能保证在局部最优解上实现收敛。

 

  本题用模拟生物算法(遗传算法)并不是最合适,我只是利用这道题简单介绍模拟生物算法的基本思想。

  欢迎各位提出本题的最优算法,空间上的最优算法、时间上的最优算法。


精彩评论:

  
#1楼   2010-01-27 14:29 |  Jianqiang Bao   
这道题目很有名哦,当年浙大考研的压轴题。

算法分析:当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。

加一个负数,会使当前和curMaxSum 变小,但只要curMaxSum 不小于0,就还有翻盘——反超maxSum的机会,所以maxSum哼着这样的小曲——我的心在等待永远在等待……

此外,如果没有正数(循环结束后maxSum为0),那么需要再次进行遍历所有元素,找到0(最大)就立刻返回,否则就比较出最大的负数来返回。
  
#2楼   2010-01-27 14:55 |  hoodlum1980   
《编程珠玑》Programming Perls 里讲过啦。
  
#3楼   2010-01-27 15:38 |  装配脑袋   
LZ这个是遗传算法。不过这道题有快速的O(N)算法哈~
  
#4楼   2010-01-27 16:25 |  极品菜鸟   
这个题很纠结
  
#5楼   2010-01-27 16:47 |  ohnoohyesohmygod   
来个代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int [] data =  new  int [] { 31, -41, 59, 26, -53, 58, 97, -93, -23, 84 };
 
int  current, max, n = 0, m = 0;
max = current = data[0];
 
for  ( int  i = 1; i < data.Length; i++)
{
     if  (current > 0)  // 第i个元素之前最大子段之和是否大于0
     {
         current = current + data[i];  //大于0则将第i个元数也加入其中,形成更大子段和
         if  (current > max)
         {
             max = current;  // 更新最大值
             m = i;
         }
     }
     else
     {
         current = data[i];  //小于0则则第i个数做为:第i个元素之前最大子段之和
         if  (current > max)
         {
             max = current;
             m = i;
             n = i;
         }
     }
}
 
Console.Out.WriteLine( "max:"  + max);
Console.Out.WriteLine( "n:"  + (n + 1));
Console.Out.WriteLine( "m:"  + (m + 1));
  
#6楼   2010-01-27 16:54 |  田景   
31,-41,59,26,-53,58,97,-93,-23,84
我的思路是这样的:
理论基础:
因为SUM(N,M)最大,那么N-1和M+1号元素一定小于0(如果N>0或者M<Length)

那么,我们可以把这个数列根据"负数"来分成几个"小段":
如上面数列就分成了:
1、31,-41,(59,26),-53,(58,97),(-93,-23),84
2、找出其中SUM最大的,上面的序列中最大的子段为:(58,97)
3、a、我们此时先假设我们的结果就是(58,97);
b、向左两两相加,如果和大于0,比如"SUM((59,26),-53)>0",那么把此段加入我们的结果集:(59,26),-53,(58,97),如果<0则继续向前相加,直到结果>0,如果向前遍历到0号元素,结果还是小于0,那么舍弃。
c、向右两两相加,同理如果和大于0,那么并入结果集,否则继续向右加:SUM(-93,-23)<0,则继续向右加SUM((-93,-23),84)<0则舍弃右边。
4、通过第3步,我们很容易地得到结果集,并且不需要递归,也不需要循环。。。
  
#7楼   2010-01-27 16:57 |  田景   
我写错了,循环是需要的。。。
  
#8楼   2010-01-27 18:05 |  strong chen   
推荐6楼的做法,采用的是动态规划做法,简洁,而且应该也是最优算法吧!
  
#9楼   2010-01-27 19:02 |  zdd   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
O(n)算法
 
public  int  Sum( int [] a)
         {
             int  curSum = 0;
             int  maxSum = 0;
             for  ( int  i = 0; i < a.Length; i++)
             {
                 if  (curSum + a[i] < 0)
                     curSum = 0;
                 else
                     maxSum = Math.Max(maxSum, curSum += a[i]);
             }
             return  maxSum;
         }
  
#10楼   2010-01-27 19:09 |  刘俊峰   
寻找最优解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
struct  SumItem
         {
             public  int  Start;
             public  int  End;
             public  int  Sum;
         }
 
         public  static  void  FindMaxSum()
         {
             int [] data =  new  int [] { 31, -41, 59, 26, -53, 58, 97, -93, -23, 84 };
 
             var  positiveIndice = ( from  in  Enumerable.Range(0, data.Length)
                                   where  data[i] > 0
                                   select  i).ToArray();
 
             List<SumItem> items =  new  List<SumItem>();
             SumItem item =  new  SumItem();
             item.Start = positiveIndice[0];
             item.End = item.Start;
             item.Sum = data[item.Start];
             bool  inPositiveRange =  true ;
             SumItem maxItem = item;
             for  ( int  i = positiveIndice[0] + 1; i <= positiveIndice[positiveIndice.Length - 1]; i++)
             {
                 if  (inPositiveRange && data[i] < 0
                     || !inPositiveRange && data[i] > 0)
                 {
                     inPositiveRange = !inPositiveRange;
 
                     items.Add(item);
                     if  (item.Sum > maxItem.Sum)
                     {
                         maxItem = item;
                     }
 
                     item =  new  SumItem();
                     item.Start = i;
                     item.End = i;
                     item.Sum = data[i];
                 }
                 else
                 {
                     item.End = i;
                     item.Sum += data[i];
                 }
             }
             items.Add(item);
             if  (item.Sum > maxItem.Sum)
             {
                 maxItem = item;
             }
 
             List<SumItem> items2 =  new  List<SumItem>();
             for  ( int  i = 0; i < items.Count - 2; i += 2)
             {
                 int  sum = items[i].Sum;
                 for  ( int  j = i + 1; j < items.Count; j++)
                 {
                     sum += items[j].Sum;
                     if  (sum > maxItem.Sum)
                     {
                         maxItem.Start = items[i].Start;
                         maxItem.End = items[j].End;
                         maxItem.Sum = sum;
                     }
                 }
             }
 
             Console.Out.WriteLine( "max:"  + maxItem.Sum);
             Console.Out.WriteLine( "n:"  + maxItem.Start);
             Console.Out.WriteLine( "m:"  + maxItem.End);
         }
  
#11楼   2010-01-27 20:19 |  ohnoohyesohmygod   
@AutumnWinter
@Junfeng Liu

没有考虑全为负数的情况:
int[] data = new int[] { -31, -41, -59, -26, -53, -58, -97, -93, -23, -84 };
  
#12楼   2010-01-27 20:49 |  徐少侠   
的确可以使用O(N)的复杂度求解

明后天来上代码吧

目标:
1、获得最优解
2、获得可能存在的多个最优解

全负数?呵呵
以上面的例子,应该是单独一个-23就是解吧?
sum(8,8)

搞搞看,
  
#13楼   2010-01-27 23:54 |  zdd   
引用 chenc:
@AutumnWinter
@Junfeng Liu

没有考虑全为负数的情况:
int[] data = new int[] { -31, -41, -59, -26, -53, -58, -97, -93, -23, -84 };

的确如此,原来的题目是如果最大和小于0,那么就算作0,而你这道题没有这个说明,所以,我的代码不满足你的题意。
  
#14楼   2010-01-28 11:34 |  zdd   
来个普通解法,二重循环,期待好算法!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public  int  MaxSum( int [] a)
{
     // define a 2-d array to hold the sum for each i and j
     // t[i, j] indicates the sum from index i to j in array a
     int  r = a.Length ;
     int  c = r;
     int [,] t =  new  int [r, c];
 
     int  maxSum =  int .MinValue;
 
     // Fix the diagonal elements
     // t[i, i] equals a[i]
     for  ( int  i = 0; i < r; i++)
     {
         t[i, i] = a[i];
         maxSum = Math.Max(maxSum, t[i, i]);
     }
 
     // foreach t[i, j], we calculate it's value by add a[j] to t[i, j - 1]
     // we only need the upper triangle elements
     for ( int  i = 0; i < r; i++)
     {
         for  ( int  j = i + 1; j < c; j++)
         {
             t[i, j] = t[i, j - 1] + a[j];
             maxSum = Math.Max(maxSum, t[i, j]);
         }
     }
 
     return  maxSum;
}
  
#15楼   2010-01-28 13:17 |  王根慧   
思路:DAtable或二维数组
先记住每个数字的位置,
然后排序一下,最后看两个数的位置即可!!
  
#17楼   2010-02-21 12:36 |  John Rambo   
感谢楼主,开眼界了.
  
#18楼   2010-02-22 12:37 |  天天不在   
6楼方法很不错啊.简洁.易懂.也是O(n)次.也是一楼思路的C#版 .
  
#19楼   2010-03-04 19:01 |  Ivan   
6楼的思路解解这个数组:
int[] data = new int[] {11,-1,10,-100,11,-10,15};
  
#20楼   2011-03-03 18:00 |  大狼熙熙   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int [] a =  new  int [12] { -1, 31, -41, 59, 26, -53, 58, 97, -93, -23, 84, -1 };
            for  ( int  i = 0; i + 1 < a.Count(); i++)
            {
                int  count = 0;
                if  (a[i] < 0 && a[i + 1] > 0)
                {
                    int  j = i + 1;
                    while  (j + 1 < a.Count())
                    {
                        count += a[j];
                        if  (a[++j] < 0)
                        {
                            Console.WriteLine( "从第"  + (i + 1) +  "到"  + (j - 1) +  "个,最大值为"  + count);
                        }
                    }
                }
            }
RE: http://www.cnblogs.com/grenet/archive/2010/01/27/1657501.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值