【问题标题】:c programming puzzlec 编程谜题
【发布时间】:2010-01-27 06:05:03
【问题描述】:

给定一个所有元素都是正数的数组,找到一个子序列的最大和,约束条件是序列中没有两个数字在数组中应该是相邻的。所以 3 2 7 10 应该返回 13(3 和 10 的总和)或 3 2 5 10 7 应该返回 15(3、5 和 7 的总和)。

我尝试使用所有可能的允许总和,然后找到最大值(蛮力方法),但有没有更好的方法。例如,对于 [3 2 7 10],我将 3,7 和 2,10 相加并取最大值。


更多示例:

  • [3, 2, 7, 1]:返回 10
  • [6, 2, 1, 4]:返回 10
  • [8, 9, 5, 1]:返回 13
  • [29, 77, 16]:返回 77
  • [29, 44, 16]:返回 45

【问题讨论】:

  • 请考虑奇偶位置有大数但不相邻的情况。我们将如何编写包含这种情况的程序例如 [239 2 3 111 1 4 546 4 3]应该给出 239+111+546+3。

标签: c algorithm


【解决方案1】:

这个问题可以通过动态规划来解决。

假设我们有一个整数数组:

i[1], i[2], i[3], ..., i[n], i[n+1], i[n+2]

我们将数组分成两部分:第一部分包含前 n 个整数,第二部分是最后两个整数:

{i[1], i[2], i[3], ..., i[n]}, {i[n+1], i[n+2]}

让我们将M_SUM(n) 表示为根据您的要求的前 n 个整数的最大总和。

会有两种情况:

  1. 如果i[n]不计入M_SUM(n),则M_SUM(n+2) = M_SUM(n) + MAX(i[n+1], i[n+2])
  2. 如果i[n]计入M_SUM(n),则M_SUM(n+2) = M_SUM(n) + i[n+2]

那么,M_SUM(n+2),我们正在寻找的值,将是以上两者中较大的值。

那么我们可以有一个非常幼稚的伪代码如下:

function M_SUM(n)
   return MAX(M_SUM(n, true), M_SUM(n, false))

function M_SUM(n, flag)
   if n == 0 then return 0
   else if n == 1
      return flag ? i[0] : 0
   } else {
      if flag then
         return MAX(
                M_SUM(n-2, true) + i[n-1], 
                M_SUM(n-2, false) + MAX(i[n-1],i[n-2]))
      else
         return MAX(M_SUM(n-2, false) + i[n-2], M_SUM(n-2, true))
   }

“flag”表示“允许使用最后一个整数”

该算法具有指数时间复杂度。

可以采用动态编程技术来消除不必要的 M_SUM 重新计算。

将每个M_SUM(n, flag) 存储到一个 n*2 矩阵中。在递归部分,如果矩阵中不存在这样的值,则计算它。否则,只需从矩阵中获取值。这会将时间复杂度降低为线性。

该算法将具有 O(n) 时间复杂度和 O(n) 空间复杂度。

【讨论】:

  • 感谢您的解决方案..动态规划的典型示例
【解决方案2】:

Python,使用动态编程的六行代码(并非如此!请参阅下面的编辑。):

def run(x):
    if len(x) == 0:
        return 0
    elif len(x) <= 2:
        return max(x)
    return max(x[0] + run(x[2:]), x[1] + run(x[3:]))

编辑和回滚:虽然上面的解决方案生成了正确的答案,但它不是动态规划。下面是一个,它使用的函数调用更少:

def main(x):
    return max(helper(x))

def helper(x):
    if len(x) == 1:
        return (0, x[0])
    a, b = helper(x[:-1])
    return (max(a, b), x[-1] + a)

【讨论】:

  • 它也会变成指数级的调用!如果 len(x) == N,那么您将使用 N-2 和 N-3 个参数重新调用该函数,因此如果 t 是您的函数所用的时间,则 t(N) > 2*t(N- 3) -- 即,只需添加三个元素,您的时间就会增加一倍。
  • 你是绝对正确的。我很感激更正。见编辑。
【解决方案3】:

举个例子[3,2,5,10,7]

使用动态规划的解决方案:

维护两个数组,如最后两行所示

alt text http://img44.imageshack.us/img44/4843/newgp.jpg

答案将是最后一列中最多两个值(红色粗体)

【讨论】:

  • 请不要使用图片作为文字。
  • 解释一下,我使用了表格,但我不知道如何插入表格。这就是我使用图像的原因。
【解决方案4】:

F# 解决方案:

let rec maxsubset = function
    | [] -> 0
    | x::[] -> x
    | x::y::xs -> max (maxsubset (y::xs)) (x + maxsubset xs)

轻松适应类 C 语言:

using System;
using System.Linq;

namespace Juliet
{
    class Program
    {
        static int MaxSubset(int[] arr, int offset)
        {
            if (offset >= arr.Length)
                return 0;
            else
                return Math.Max(MaxSubset(arr, offset + 1), arr[offset] + MaxSubset(arr, offset + 2));
        }

        static void Test(params int[] nums)
        {
            Console.WriteLine("----------------");
            Console.WriteLine("MaxSubset({0}) = {1}", String.Join(", ", nums), MaxSubset(nums, 0));
        }

        static void Main(string[] args)
        {
            Test(3, 2, 7, 1);
            Test(6, 2, 1, 4);
            Test(8, 9, 5, 1);
            Test(29, 77, 16);
            Test(29, 44, 16);
            Test(239, 2, 3, 111, 1, 4, 546, 4, 3);
            Test(100, 1, 1, 100);
            Console.ReadLine();
        }
    }
}

【讨论】:

    【解决方案5】:
    sum[0] = 0;
    sum[1] = 0;
    
    for(i = 0; i < arraylength; i++) 
        sum[i & 1] += array[i];
    
    printf("sum = %u\n", MAX(sum[0], sum[1]));
    printf("homework completed\n");
    

    【讨论】:

    • 反例:100 1 1 100
    【解决方案6】:

    可爱的问题。最简单的方法似乎是迭代地考虑数组,保持两个迄今为止最好的总和:使用 a[i] 时可以获得的最佳总和是允许的,使用 a[i] 时可以获得的最佳总和可能是不允许。在python中:

    def best(arr):
        # best sums of zero length array are 0
        ok, bad = 0,0 
    
        for x in arr:
            # best sum if we can use x is to use it,
            # because all elements are positive
            ok += x
    
            # moving along one step means we can't
            # use the ok sum, and can use the bad sum
            bad, ok = ok, bad
    
            # but just because we could use ok, doesn't
            # mean we have to, so if it's better, use
            # that path
            if bad < ok:
                bad = ok
    
        # bad is then our best possible sum
        return bad
    

    【讨论】:

    • 这一行是做什么的;是否交换值:bad,ok=ok,bad
    • 代码错误。考虑示例 {1, 2, 3, 4}。让ok=bad=0。第一次迭代后,{ok,bad}={0,1}。在第二次迭代之后,它是 {1,2}。第三次迭代后,{4,4}。在第四次迭代之后,{8,8}。您的算法返回 8,这是不正确的。在这个例子中,它应该是 2 + 4 = 6。
    • best([1,2,3,4]) 正确返回 6。如果您在该示例的每个循环末尾添加 print ok,bad,您将得到: 0,1; 1,2; 2,4;和 4,6。
    【解决方案7】:
    int findSum(int* arr, int sz)
    {
        if( sz <= 0) return 0;
    
        if( sz == 1)
        {
            return arr[0];
        }
        else
        {
            int a = arr[0] + findSum(arr+2, sz-2); 
    
            int b = findSum(arr+1, sz-1);
    
            if( a >= b)
                return a;
            else 
                return b;
        }
    }
    

    【讨论】:

      【解决方案8】:

      考虑中间元素,它可能是解决方案的一部分,也可能不是。对于每种情况,找到左侧和右侧剩余子列表的最佳解决方案并将它们组合起来,然后从两种情况中选择更好的解决方案。

      【讨论】:

      • 以及如何为左右子列表特别是找到最佳解决方案。当奇数和偶数位置有大量数字时
      【解决方案9】:
      {
      
      int a[10]={1,2,3,4,5,6,7,89,8,9};
      
      int k=0,i,j,l;
      int sum[36],max;
      
      for (i=0;i<10;i++)
      {
      for (j=i+2;j<10;j++,k++)
      sum[k]=a[i]+a[j];
      }
      max=a[0];
      for(i=0;i<36;i++)
      printf("sum[%d] is %d\n",i,sum[i]);
      
      for(l=1;l<36;l++)
      {
      if(max>sum[l])
      continue;
      else
      max=sum[l];
      }
      printf("%d is the max sum",max);
      }
      

      【讨论】:

        【解决方案10】:
        int choose( int n)
        {
           if((n==1) || (n==0))
               return array[n];
           if( n==2)
               return array[0];
        
           totalSum += max(choose(n-2), choose(n-3));
        }
        

        max 是一个函数,用于获取两者中的最大值。

        对于ARRAY“数组”,对数组的每个元素进行函数调用,并将最大结果存储在另一个数组中(比如arrayOfMax[n])

        【讨论】:

          【解决方案11】:

          包 com.dan.alg.recursion;

          /** * 问题:给定一个正数数组,求子序列的最大和 * 具有序列中没有 2 个数字在数组中应该相邻的约束。 * 所以 3 2 7 10 应该返回 13(3 和 10 的总和)或 3 2 5 10 7 应该返回 15 *(3、5 和 7 的总和)。以最有效的方式回答问题。 *

          * 解决方案:我们将递归地反向构建解决方案(从最后一个位置开始 * 的数组并以我们的方式工作到它的开头)b基于以下观察: *

          * 位置 p 的最大和可以从以下两个值的最大值中获得: * V1 = 位置 p 的值 + 位置 p 的最大总和 - 2(请记住,两个元素不能相邻) * V2 = 位置 p - 1 的最大总和 * * @作者丹 */

          公共类 MaxSumNoNeighbours {

          private static int [] arr = { 29, 44, 16 };
          
          /**
           * Determine the max sum for the current position.
           * 
           * @param currentPos    the current position in the array.
           */
          private static int maxSum(final int currentPos) {
              //  The sum is zero if we are outside of the bounds.
              if (currentPos < 0) {
                  return 0;
              }
          
              return Math.max(
                      arr[currentPos] + maxSum(currentPos - 2), 
                      maxSum(currentPos - 1));
          }
          
          public static void main (final String [] args) {
              //  Start from the end of the array and work your way forwards
              System.out.println(maxSum(arr.length - 1));
          }
          

          }

          【讨论】:

            猜你喜欢
            • 2014-12-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-11
            • 2021-11-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多