从对象列表中查找组合并根据属性值对其进行排序

时间:2017-08-21 10:21:54

标签: c#

我有一个CardSlot类型的列表,每个CardSlot对象都包含不同的卡,比如Card1,Card2,Card3(数字可能不同)。

每个CardSlot对象都预设了最大数量的Card1,Card2和Card3。现在,用户输入所需的卡数量,并且应该获得满足标准的CardSlot组合。

例如:

 List<CardSlot> listCardSlot = new List<CardSlot>();
 CardSlot cardSlot1 = new CardSlot();
 cardSlot1.Name = "cardSlot1";
 cardSlot1.Price = 900;
 cardSlot1.Card1 = 2;
 cardSlot1.Card2 = 3;
 cardSlot1.Card3 = 4;
 listCardSlot.Add(cardSlot1);

 CardSlot cardSlot2 = new CardSlot();
 cardSlot2.Name = "cardSlot2";
 cardSlot2.Price = 850;
 cardSlot2.Card1 = 3;
 cardSlot2.Card2 = 2;
 cardSlot2.Card3 = 4;
 listCardSlot.Add(cardSlot2);

 CardSlot cardSlot3 = new CardSlot();
 cardSlot3.Name = "cardSlot3";
 cardSlot3.Price = 950;
 cardSlot3.Card1 = 4;
 cardSlot3.Card2 = 3;
 cardSlot3.Card3 = 2;
 listCardSlot.Add(cardSlot3);

现在,如果用户输入Card1 = 4,Card2 = 5且Card3 = 4,最终结果应该是CardSlot对象的组合,价格最低。

有人可以给我一个正确方向的推动吗?请告诉我,如果有什么不清楚的地方,我会尝试改进它。

修改

我尝试使用以下功能查找列表中的所有可能组合:

public static List<List<T>> ItemCombinations<T>(List<T> inputList, int minimumItems = 1)
    {
        int nonEmptyCombinations = (int)Math.Pow(2, inputList.Count) - 1;
        List<List<T>> listOfCombinations = new List<List<T>>(nonEmptyCombinations + 1);

        if (minimumItems == 0)  
            listOfCombinations.Add(new List<T>());

        for (int i = 1; i <= nonEmptyCombinations; i++)
        {
            List<T> thisCombination = new List<T>(inputList.Count);
            for (int j = 0; j < inputList.Count; j++)
            {
                if ((i >> j) % 2 != 0)
                    thisCombination.Add(inputList[j]);
            }

            if (thisCombination.Count >= minimumItems)
                listOfCombinations.Add(thisCombination);
        }

        return listOfCombinations;
    }

这会成功返回所有可能的组合。但是,它仍然没有考虑重复出现同一卡槽可能是正确选择的情况。例如,在上述场景中,正确的选择是1 X CardSlot1 + 1 X CardSlot2

修改2

我达到了一个中间解决方案,并将其作为答案发布,在混合和匹配组合的情况下,它仍然没有给我所需的答案。任何人都可以看看并为此提出建议吗?

2 个答案:

答案 0 :(得分:0)

所以我已经为此做了一个解决方案,必须说这对我来说是个头疼,但是我已经尝试过了。

CardSlot 我上课:

public class CardSlot
{
    public string Name { get; set; } = "";
    public double Price { get; set; } = 0;
    public int Card1 { get; set; } = 0;
    public int Card2 { get; set; } = 0;
    public int Card3 { get; set; } = 0;
}

然后其余部分如下:

  //your code
  //as above
  //in the question
  listCardSlot.Add(cardSlot3); //your code

  ValueChecker(1);

  double lowestamount = mycards.Any() ? mycards.Min(item => item.Price) : 0;

  MessageBox.Show("You need " + multiplier.ToString() + " " + 
                   ReturnLowestCardSlot(lowestamount).Name);
}

int a = 4, b = 5, c = 4; //User entered values

int multiplier = 0;

List<CardSlot> mycards = new List<CardSlot>();
CardSlot ReturnLowestCardSlot(double lowestPrice)
{
    CardSlot cardslot = null;
    foreach (var item in mycards)
    {
        if (item.Price == lowestPrice)
        {
            cardslot = item;
            break;
        }
    }
    return cardslot;
}

void ValueChecker(int increment)
{
    multiplier += increment;
    foreach (CardSlot item in listCardSlot)
    {
        if (((multiplier * item.Card1) >= a) &&
            ((multiplier * item.Card2) >= b) && 
            ((multiplier * item.Card3) >= c))
        {
            if (!(mycards.Contains(item)))
                mycards.Add(item);
        }
    }
    if (mycards.Count == 0)
       ValueChecker(1);
  }

我得到的输出是:

  

你需要2张cardSlot1

希望你能以某种方式调整这个以满足你的需要

编辑
我看到你希望输出为2 cardslot2,其中包含:

cardSlot2.Card1 = 3;
cardSlot2.Card2 = 2;
cardSlot2.Card3 = 4;

2 x CardSlot2将给出:

cardSlot2.Card1 = 6;
cardSlot2.Card2 = 4;
cardSlot2.Card3 = 8;

但用户的要求是usercard1 = 4usercard1 = 5&amp; usercard1 = 4
此处Card2 = 4仍然小于usercard2 = 5
我不知道它是如何满足要求的。

编辑2

你的评论是对的,我没有这样做。现在这将给你结果:

  //codes same
  //as your question
  ValueChecker(1);

  double lowestamount = mycards.Any() ? mycards.Min(item => item.Price) : 0;

  CardSlot leastcard = ReturnLowestCardSlot(lowestamount, mycards);

  double lowestamount2 = listCardSlot.Any() ? listCardSlot.Min(item => item.Price) : 0;

  CardSlot lowestcard = ReturnLowestCardSlot(lowestamount2, listCardSlot);

  if ((leastcard.Price + lowestcard.Price) > (multiplier * leastcard.Price))
       MessageBox.Show("You need " + multiplier.ToString() + " " + leastcard.Name);

  else
       MessageBox.Show("You need 1 " + leastcard.Name + " and 1 " + lowestcard.Name);
}

并修改了ReturnLowestCardSlot

CardSlot ReturnLowestCardSlot(double lowestPrice, List<CardSlot> list)
{
    CardSlot cardslot = null;
    foreach (var item in list)
    {
        if (item.Price == lowestPrice)
        {
            cardslot = item;
            break;
        }
    }
    return cardslot;
}

输出:

  

您需要1张cardSlot1和1张cardSlot2

答案 1 :(得分:0)

感谢Nobody(这听起来很粗鲁:D),这是我达到的中间(尽管不是非常优化)解决方案。这给出了最便宜的组合(CardSlots列表)。

但是,当答案应该是异质解决方案时(例如5 X CardSlot1,3 X CardSlot2,1 X CardSlot3),它不会提供答案。对此有任何帮助将非常感激。

    static void Main(string[] args)
    {
        //Some Code
        // Get user input card1, card2, card3
        List<List<CardSlot>> validCombination = GetAllCombinations(card1, card2, card3, listCardSlot);

        List<CardSlot> CheapestCombo = GetLeastPricedCombination(validCombination);
        //Here I get the cheapest homogenous combination 
    }

    private static List<List<CardSlot>> GetAllCombinations(int card1, int card2, int card3, List<CardSlot> listCardSlot)
    {

        var listOfCombinations = ItemCombinations(listCardSlot, 1);

        List<List<CardSlot>> validCombination = new List<List<CardSlot>>();
        int multiplier;

        foreach (List<CardSlot> combination in listOfCombinations)
        {
            multiplier = 1;
            if (CheckMaterialSum(combination, card1, card2, card3, ref multiplier))
            {
                validCombination.Add(combination);
            }
            else
            {
                GetMultiplierCombination(combination, card1, card2, card3, ref validCombination, ref multiplier);
            }
        }

        return validCombination;
    }

    private static bool CheckMaterialSum(List<CardSlot> combination, int card1, int card2, int card3, ref int multiplier)
    {
        int sumcard1 = multiplier * (combination.Sum(comb => comb.Card1));
        int sumcard2 = multiplier * (combination.Sum(comb => comb.Card2));
        int sumcard3 = multiplier * (combination.Sum(comb => comb.Card3));

        if (sumcard1 >= card1 && sumcard2 >= card2 && sumcard3 >= card3)
        {
            return true;
        }
        return false;
    }

    private static void GetMultiplierCombination(List<CardSlot> combination, int card1, int card2, int card3, ref List<List<CardSlot>> validCombination, ref int multiplier)
    {
        while (!CheckMaterialSum(combination, card1, card2, card3, ref multiplier))
        {
            multiplier += 1;
        }
        List<CardSlot> interMediateCombo = new List<CardSlot>();
        for (int i = 0; i < multiplier; i++)
        {
            interMediateCombo.AddRange(combination);
        }
        validCombination.Add(interMediateCombo);
    }


    private static List<CardSlot> GetLeastPricedCombination(List<List<CardSlot>> validCombination)
    {
        List<CardSlot> cheapestCombo = new List<CardSlot>();
        int leastPrice = int.MaxValue;
        int priceTotal;

        //Find better way for finding least priced combination
        foreach (List<CardSlot> combination in validCombination)
        {
            priceTotal = combination.Sum(combo => combo.Price);

            if (priceTotal < leastPrice)
            {
                leastPrice = priceTotal;
            }
        }

        foreach (List<CardSlot> combination in validCombination)
        {
            priceTotal = combination.Sum(combo => combo.Price);

            if(priceTotal == leastPrice)
            {
                cheapestCombo.AddRange(combination);
                break;
            }
        }

        return cheapestCombo;
    }