简体   繁体   English

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

[英]Share Fruits Fairly (Dynamic Programming)

I'm having a very hard time trying to figure out how to solve this problem efficiently. 我正在努力弄清楚如何有效地解决这个问题。 Let me describe how it goes: 让我来描述一下:

"A hard working mom bought several fruits with different nutritional values for her 3 kids, Amelia, Jessica and Bruno. Both girls are overweight, and they are very vicious and always leave poor Bruno with nothing, so their mother decided to share the food in the following manner: “一个努力工作的妈妈为她的3个孩子Amelia,Jessica和Bruno购买了几种具有不同营养价值的水果。两个女孩都超重,而且非常恶毒,总是让可怜的布鲁诺一无所有,所以他们的母亲决定分享这些食物。方式如下:

  • Amelia being the heaviest one gets the most amount of Nutritional Value Amelia是最重的一个获得最大的营养价值
  • Jessica gets an amount equal or less than Amelia 杰西卡获得的金额等于或低于阿米莉亚
  • Bruno gets an amount equal or less than Jessica, but you need to find a way to give him the highest possible nutritional value while respecting the rule ( A >= J >= B )" 布鲁诺获得的金额等于或小于杰西卡,但你需要找到一种方法,在尊重规则的同时给予他最大的营养价值(A> = J> = B)“

Note: The original problem is described differently but the idea is the same, I don't want my classmates to find this post when they google for help hehe. 注意:原始问题的描述不同,但想法是一样的,我不希望我的同学在谷歌寻求帮助时找到这篇文章嘿嘿。

One of the test cases given by my teacher is the following: 我老师给出的一个测试用例如下:

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

Note: I am aware that there are several ways of diving the fruits among the kids, but it doesn't really matter as long as it follows the rule A >= J >= B. 注意:我知道有几种方法可以让孩子们吃水果,但只要遵循规则A> = J> = B,这并不重要。

At first I made an algorithm that generated all the subsets, each one had their sums, and the positions that were in use. 起初我做了一个生成所有子集的算法,每个子集都有它们的总和,以及正在使用的位置。 That method was quickly discarded because the list of fruits can have up to 50 fruits, and the subset algorithm is O(2^n). 该方法很快被丢弃,因为水果列表最多可以有50个水果,子集算法是O(2 ^ n)。 I ran out of memory. 我的内存耗尽了。

The second idea that I have is to use Dynamic Programming. 我的第二个想法是使用动态编程。 In the columns header I would have the positions of the Fruit's List, in the row header the same, it's kind of hard to explain with letters so I'll ahead an do the table for the previous example { 4, 2, 1, 8, 11, 5, 1}. 在列标题中我将拥有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

Each time we advance to the row below we add the positions 1,2,3,...,7 每当我们前进到下面的行时,我们添加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)

Now that you know how it goes lets add the first row 既然你知道它是怎么回事就可以添加第一行

   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                     

I put the 00 because the 1st position is already in use. 我把00放了,因为第一个位置已经在使用了。 The completed table would look like this. 完成的表格看起来像这样。

   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    

Now that we have the table. 现在我们有了桌子。 I divide the sum of the Nutritional Values by the amount of kids, 32/3 = 10.6667, the ceiling would be 11. I try to check for 11, if it's in the table, I choose it and mark the position of the row and columns of the tables as used, then I would try to check for 11 again, if it's in the table I choose it otherwise look the 10, or 9, etc until I find it. 我将营养价值的总和除以孩子的数量,32/3 = 10.6667,上限为11.我试着检查11,如果它在表中,我选择它并标记行的位置和使用的表的列,然后我会尝试再次检查11,如果它在表中我选择它,否则看10或9等,直到我找到它。 Afterwards I would mark the respective positions as used then sum the unused positions to get Bruno's fruits. 然后,我会标记所使用的各自位置,然后将未使用的位置相加以获得布鲁诺的果实。

I know that there has to be better way to do this because I found a flaw in my method, the table only has the sum of a few subsets. 我知道必须有更好的方法来做到这一点,因为我在我的方法中发现了一个缺陷,表只有几个子集的总和。 So maybe that will be detrimental in a few test cases. 因此,在一些测试案例中,这可能会有害。 Maybe a 3D Memoization Cube?, I think it would consume too much memory, and I have a limit too 256MB. 也许是3D Memoization Cube?,我认为它会消耗太多内存,而且我的限制也是256MB。

Wow, I didn't realize I typed this much xX I hope I don't get a lot of tl; 哇,我没有意识到我输入了这么多xX我希望我不会得到很多tl; dr. 博士。 Any help / guide would be greatly appreciated :D 任何帮助/指南将不胜感激:D

EDIT: I made the code that generates the table in case anyone wants to try it out. 编辑:我制作了生成表格的代码,以防有人想尝试一下。

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();
        }

    }
    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);
    }

This works for fruits with relatively similar values. 这适用于具有相对相似值的水果。 If you add a fruit whose value is greater than all other fruits combined (which I did once by accident) it breaks down a bit. 如果你添加的水果的价值大于所有其他水果的总和(我偶然做过一次)它会分解一些。

A slightly computationally intensive way would be to assign the fruit in a round robin way, starting with the highest nutritional value for amelia. 一种略微计算密集的方式是以循环方式分配水果,从amelia的最高营养价值开始。
From there, progressively loop through the fruit from lowest nutritional value held by amelia, and try each combination of either (a) giving the fruit to jessica, or (b) swapping the fruit with one held by jessica, while still satisfying the rule. 从那里开始,逐渐从阿米莉亚所持有的最低营养价值中循环出水果,并尝试(a)将水果交给杰西卡,或(b)用杰西卡持有的水果交换水果,同时仍然满足规则。 Then apply the same method to jessica and bruno. 然后将相同的方法应用于jessica和bruno。 Repeat these 2 loops until no more swaps or gives are possible. 重复这两个循环,直到不再进行交换或给出。
Slightly trickier, but potentially faster, would be to simultaneously give/swap to jess/bruno. 稍微棘手,但可能更快,将同时给/交换jess / bruno。 For each 2 pieces of fruit that A holds, you would have 4 options to try, with more if you at the same time try and balance J & B. 对于A持有的每2件水果,您将有4个选项可供尝试,如果您同时尝试并平衡J&B,则需要更多。

For a faster algorithm, you could try asking at the mathematics stack exchange site, as this is very much a set-theory problem. 对于更快的算法,你可以尝试在数学堆栈交换站点询问,因为这是一个集理论问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM