我正在开发具有以下要求的支付系统游戏:
1-假设游戏中的账单是:10,5,4,3,2,1
2- AI需要选择所需的最少数量的账单来支付确切的金额,即如果需要支付8并且AI有(4,4,3,3,2)......他可以选择( 4,4)但不是(3,3,2)
3-如果AI无法使用他所拥有的账单赚取确切的金额,他应该选择这样的组合,使得它给出具有最小差值的金额,即,如果所需的支付金额是7和AI有下面的账单(10,5,4,4),他选择了(4,4)给玩家1超过所需金额。
以下是我的代码
//sortedValues is a list containing my bills in descending order
//ChosenCardsToPay is a list for the bills I choose to pay with
public void PreparePayment(int neededAmount)
{
int remainingAmount = neededAmount;
int chosenAmount;
while (remainingAmount > 0)
{
chosenAmount = 0;
foreach (int moneyValue in sortedValues )
{
if (moneyValue <= remainingAmount)
{ chosenCardsToPay.Add (moneyValue); //Add Bill Value to my candidate list
remainingAmount = remainingAmount - moneyValue;
chosenAmount = moneyValue;
break;
}
}
if (chosenAmount != 0)
sortedValues.Remove (moneyValue);//Remove Chosen Bill from Initial List
else //If all bill values are greater than remaining amount, i choose the bill with smallest value and add to the candidate list
{
chosenAmount = sortedValues.Last();
sortedValues.Remove(chosenAmount);
chosenCardsToPay.Add (chosenAmount);
remainingAmount = remainingAmount - moneyValue;
}
}
}
大部分时间都可以正常工作,但请注意这种情况:所需金额为4,AI有(3,2,2)作为账单。使用上述算法,AI选择(3,2)最佳答案为(2,2)。
有人可以指导我正确思考这个问题吗?谢谢!
答案 0 :(得分:1)
这是我提出的递归解决方案。我们的想法是跟踪“过剩”并在您找到完全匹配时立即返回。如果没有找到完全匹配,你只需根据它们的数量,然后根据需要多少账单来计算超额数量,然后选择第一个账单。为了在完全匹配上获得最少的账单,请确保bills
按降序排序。如果没有办法用给定的一组账单来支付金额,这也将返回一个空序列。
public static IEnumerable<int> CoverAmount(
int amount, List<int> bills, HashSet<int> used = null)
{
if (used == null)
used = new HashSet<int>();
if (amount <= 0)
return Enumerable.Empty<int>();
var overages = new List<Tuple<List<int>, int>>();
for(int index = 0; index < bills.Count; index++)
{
var bill = bills[index];
if (used.Contains(index))
continue;
if (bill > amount)
{
overages.Add(Tuple.Create(new List<int> { bill }, bill - amount));
}
else if (bill == amount)
{
return Enumerable.Repeat(bill, 1);
}
else
{
used.Add(index);
var bestSub = CoverAmount(amount - bill, bills, used).ToList();
used.Remove(index);
bestSub.Add(bill);
var sum = bestSub.Sum();
if (sum == amount)
{
return bestSub;
}
if (sum > amount)
{
overages.Add(Tuple.Create(bestSub, sum - amount));
}
}
}
return overages
.OrderBy(t => t.Item2)
.ThenBy(t => t.Item1.Count)
.FirstOrDefault()?.Item1 ?? Enumerable.Empty<int>();
// OR this if you are not using C# 6
// var bestOverage = overages
// .OrderBy(t => t.Item2)
// .ThenBy(t => t.Item1.Count)
// .FirstOrDefault();
// return bestOverage == null ? Enumerable.Empty<int>() : bestOverage.Item1;
}
以下代码
Console.WriteLine(string.Join(", ", CoverAmount(8, new List<int> { 4, 4, 3, 3, 2 })));
Console.WriteLine(string.Join(", ", CoverAmount(7, new List<int> { 10, 5, 4, 4 })));
Console.WriteLine(string.Join(", ", CoverAmount(4, new List<int> { 3, 2, 2 })));
Console.WriteLine(string.Join(", ", CoverAmount(10, new List<int> { 11, 6, 5 })));
将产生此输出
4,4
4,4
2,2
11