【问题标题】:Share Fruits Fairly (Dynamic Programming)公平分享水果(动态编程)
【发布时间】:2012-07-02 01:02:09
【问题描述】:

我很难弄清楚如何有效地解决这个问题。让我描述一下它是怎么回事:

“一位勤劳的妈妈为她的三个孩子Amelia、Jessica和Bruno买了几种营养价值不同的水果。两个女孩都超重,而且她们非常恶毒,总是让可怜的Bruno一无所有,所以他们的妈妈决定分享食物按以下方式:

  • 阿米莉亚体重最重,营养价值最高
  • Jessica 得到的金额等于或小于 Amelia
  • Bruno 得到的食物量等于或少于 Jessica,但您需要找到一种方法,在遵守规则 (A >= J >= B) 的同时为他提供尽可能高的营养价值"

注意:原问题描述不同但思路是一样的,我不希望我的同学在google寻求帮助时找到这个帖子嘿嘿。

我老师给的一个测试用例如下:

The fruit list has the following values { 4, 2, 1, 8, 11, 5, 1}

Input:
7   -----> Number of Fruits
4 2 1 8 11 5 1 ----> Fruits Nutritional Values

Output:
1 11  ---->  One fruit, their nutritional values sum for Amelia
5     ---->  Position of the fruit in the list
3 11  ---->  Three fruits, their nutritional values sum for Jessica
1 2 6 ---->  Position of the fruits in the list
3 10  ---->  Three fruits, their nutritional values sum for Bruno
3 4 7 ---->  Position of the fruits in the list

注意:我知道在孩子们中间有几种挖水果的方法,但只要遵循规则 A >= J >= B,这并不重要。

起初我做了一个算法来生成所有子集,每个子​​集都有它们的总和,以及正在使用的位置。该方法很快被丢弃,因为水果列表最多可以有 50 个水果,并且子集算法是 O(2^n)。我的内存用完了。

我的第二个想法是使用动态编程。在列标题中,我将有水果列表的位置,在行标题中相同,用字母很难解释,所以我会提前为前面的例子做表格 { 4, 2, 1, 8 , 11, 5, 1}。

   00 01 02 03 04 05 06 07
00 
01
02
03
04
05
06
07

每次我们前进到下面的行,我们都会添加位置 1,2,3,...,7

   00 01 02 03 04 05 06 07
00 00                     <---No positions in use  
01 04                     <---RowPosition 1 + Column Position(Column 0) (4+0)
02 06                     <---RowPosition 1 + RowPosition 2 + Column Position (4+2+0)
03 07                     <---RP(1) + RP(2) + RP(3) + CP(0) (4+2+1+0)
04 15                     <--- (4+2+1+8+0)
05 26
06 31
07 32                     <--- (4+2+1+8+11+5+1+0)

现在你知道它是怎么回事了,让我们添加第一行

   00 01 02 03 04 05 06 07
00 00 04 02 01 08 11 05 01   <-- Sum of RP + CP                     
01 04 00 06 05 12 15 09 05   <-- Sum of RP(0..1) + CP                      
02 06                     
03 07                     
04 15                     
05 26
06 31
07 32                     

我放了 00,因为第一个位置已经在使用中。完成的表格将如下所示。

   00 01 02 03 04 05 06 07
00 00 04 02 01 08 11 05 01                      
01 04 00 06 05 12 15 09 05                         
02 06 00 00 07 14 17 11 07                 
03 07 00 00 00 15 18 12 08                  
04 15 00 00 00 00 26 20 16                    
05 26 00 00 00 00 00 31 27
06 31 00 00 00 00 00 00 32
07 32 00 00 00 00 00 00 00    

现在我们有了桌子。我将营养价值的总和除以孩子的数量,32/3 = 10.6667,上限为 11。我尝试检查 11,如果它在表中,我选择它并标记行的位置和使用的表的列,然后我会尝试再次检查 11,如果它在表中,我会选择它,否则查看 10 或 9 等,直到找到它。之后,我会将相应的位置标记为已使用,然后将未使用的位置相加以获得布鲁诺的果实。

我知道必须有更好的方法来做到这一点,因为我发现我的方法存在缺陷,该表只有几个子集的总和。所以也许这在一些测试用例中是有害的。也许是 3D Memoization Cube?,我认为它会消耗太多内存,而且我的限制也是 256MB。

哇,我没有意识到我输入了这么多 x.X。我希望我不会得到很多 tl;博士。任何帮助/指南将不胜感激:D

编辑:我编写了生成表格的代码,以防有人想尝试。

static void TableGen(int[] Fruits)
    {
        int n = Fruits.Length + 1;
        int[,] memo = new int[n, n];

        for (int i = 1; i < n; i++)
        {
            memo[0, i] = Fruits[i - 1]; 
            memo[i, 0] = memo[i - 1, 0] + Fruits[i - 1]; 

            for (int j = i + 1; j < n; j++)
                memo[i, j] = memo[i, 0] + Fruits[j - 1];               
        }


        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
                Console.Write("{0:00} ", memo[i, j]);

            Console.WriteLine();
        }

    }

【问题讨论】:

  • 示例输出不满足指定规则。
  • “Amelia 是最重的人得到最多的水果”这意味着你需要给她最多的营养价值或 FruitCount?
  • 您是否还有其他规则被省略?否则,只需给 Amelia 最高 NV 项目,Jessica 次之,Bruno 次之。重复直到你没有食物。这将为您提供 A >= J >= B,但不会使它们的总值尽可能接近。
  • 我修正了我说“阿米莉亚是最重的人得到最多的水果”的规则,而它应该是营养价值。
  • 这个问题本身就是疯了。这个前提是人为的,没有通过现实世界的气味测试。案例中的母亲应该让两个超重的女孩严格节食,并让专业人士对她们的情绪/心理障碍进行检查。布鲁诺应该被送去和他的祖父母住在一起。如果你的老师想出了这个问题,请让他/她想出更好的故事,因为这个很烂。

标签: c# dynamic-programming


【解决方案1】:
    for(i = 0; i < count; i++)
    {
    currentFruit=Fruits.Max();

    if(Amelia.Sum() + currentFruit < Jessica.Sum() + currentFruit)
        {
        Amelia.Add(currentFruit);
        Fruits.Remove(currentFruit);
        continue;
        }
    if(Jessica.Sum() + currentFruit < Bruno.Sum() + currentFruit)
        {
        Jessica.Add(currentFruit);
        Fruits.Remove(currentFruit);
        continue;
        }
    Bruno.Add(currentFruit);
    Fruits.Remove(currentFruit);
    }

这适用于具有相对相似值的水果。如果您添加的水果的价值大于所有其他水果的总和(我偶然做了一次),它会有点崩溃。

【讨论】:

  • 创建网格似乎很麻烦……为什么?
【解决方案2】:

一种稍微计算密集型的方法是以循环方式分配水果,从阿米莉亚的最高营养价值开始。
从那里开始,逐步遍历 amelia 持有的最低营养价值的水果,并尝试以下两种组合:(a) 将水果交给 jessica,或 (b) 将水果与 jessica 持有的水果交换,同时仍满足规则。 然后对 jessica 和 bruno 应用相同的方法。重复这 2 个循环,直到无法再进行交换或给予。
稍微棘手但可能更快的方法是同时给予/交换给 jess/bruno。对于 A 持有的每 2 个水果,您将有 4 个选项可供尝试,如果您同时尝试平衡 J & B,则可以选择更多。

对于更快的算法,您可以尝试在数学堆栈交换站点上询问,因为这在很大程度上是一个集合论问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 1970-01-01
    • 2015-03-20
    • 2010-09-24
    • 1970-01-01
    相关资源
    最近更新 更多