公平分享水果(动态编程)

时间:2012-07-01 04:59:10

标签: c# dynamic-programming

我很难找到如何有效地解决这个问题。让我来描述一下:

“一位努力工作的妈妈为她的3个孩子Amelia,Jessica和Bruno购买了几种具有不同营养价值的水果。两个女孩都超重,他们非常恶毒,总是让可怜的布鲁诺一无所有,所以他们的母亲决定分享食物以下列方式:

  • Amelia是最重的人,营养价值最高
  • 杰西卡获得的金额等于或低于Amelia
  • 布鲁诺获得的金额等于或小于杰西卡,但你需要找到一种方法,在尊重规则的同时给予他最大的营养价值(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)。我的内存耗尽了。

我的第二个想法是使用动态编程。在列标题中我将拥有Fruit's List的位置,在行标题中相同,用字母很难解释所以我将提前做一个表格用于上一个例子{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.我希望我不会得到很多东西;博士。任何帮助/指南将不胜感激: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();
        }

    }

2 个答案:

答案 0 :(得分: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);
    }

这适用于具有相对相似值的水果。如果你添加的水果的价值大于所有其他水果的总和(我偶然做过一次)它会分解一点。

答案 1 :(得分:1)

略微计算密集的方式是以循环方式分配水果,从amelia的最高营养价值开始。
从那里开始,逐渐从阿米莉亚所持有的最低营养价值中循环出水果,并尝试(a)将水果交给杰西卡,或(b)用杰西卡持有的水果交换水果,同时仍然满足规则。 然后将相同的方法应用于jessica和bruno。重复这两个循环,直到不再进行交换或给出 稍微棘手,但可能更快,将同时给/交换jess / bruno。对于A持有的每2件水果,您将有4种选择尝试,如果您同时尝试和平衡J&amp; B.

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