你好我正在尝试创建一个algorythm,找出我可以改变回来的方式。 但我只是无法正确实现,我一直得到4,我应该得到6,我只是不明白为什么。
这是我在C#中的实现,它是从http://www.algorithmist.com/index.php/Coin_Change
的伪代码创建的 private static int[] S = { 1, 2, 5 };
private static void Main(string[] args)
{
int amount = 7;
int ways = count2(amount, S.Length);
Console.WriteLine("Ways to make change for " + amount + " kr: " + ways.ToString());
Console.ReadLine();
}
static int count2(int n, int m)
{
int[,] table = new int[n,m];
for (int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
// Rules
// 1: table[0,0] or table[0,x] = 1
// 2: talbe[i <= -1, x] = 0
// 3: table[x, j <= -1] = 0
int total = 0;
// first sub-problem
// count(n, m-1)
if (i == 0) // rule 1
total += 1;
else if (i <= -1) // rule 2
total += 0;
else if (j - 1 <= -1)
total += 0;
else
total += table[i, j-1];
// second sub-problem
// count(n-S[m], m)
if (j - 1 <= -1) // rule 3
total += 0;
else if (i - S[j - 1] == 0) // rule 1
total += 1;
else if (i - S[j - 1] <= -1) // rule 2
total += 0;
else
total += table[i - S[j-1], j];
table[i, j] = total;
}
}
return table[n-1, m-1];
}
答案 0 :(得分:5)
出于纯粹的深夜无聊(是的,这肯定需要精炼)...它使用递归,linq和一次性产生并且具有与代码行一样多的括号,所以我很对此感到不满......
static IEnumerable<List<int>> GetChange(int target, IQueryable<int> coins)
{
var availableCoins = from c in coins where c <= target select c;
if (!availableCoins.Any())
{
yield return new List<int>();
}
else
{
foreach (var thisCoin in availableCoins)
{
foreach (var result in GetChange(target - thisCoin, from c in availableCoins where c <= thisCoin select c))
{
result.Add(thisCoin);
yield return result;
}
}
}
}
答案 1 :(得分:2)
如果您解释了算法是如何工作的,那将会非常有用。如果没有评论且变量仅以n
,m
,i
命名,则很难理解其目的。例如,在迭代各种类型的硬币时,您应该使用更具描述性的名称,例如coinType
。
但是,有些地方对我来说很可疑。例如,您的变量i
和j
会迭代范围1 .. m
或1 .. n
中的值 - 它们总是积极的。这意味着您的规则2和3永远不会运行:
// ...
else if (i <= -1) // <- can never be 'true'
total += 0;
else if (j - 1 <= -1) // <- 'true' when 'j == 0'
// ...
if (j - 1 <= -1) // <- can never be 'true'
// ...
i
永远不会小于或等于-1
和j
。
答案 2 :(得分:2)
这是运行顺序的算法。按绿色“播放”箭头运行它。 http://www.exorithm.com/algorithm/view/make_change
我认为主要问题是算法循环应该从-1开始。我认为递归写得更清楚,但这是一个有趣的练习。
答案 3 :(得分:1)
一些观察结果。
1)如果将count
函数从伪代码移动到单独的子例程中,它将使代码更简单(并且更不容易出错)。像这样的东西(基于链接中的伪代码)
func count(table, i, j)
if ( i == 0 )
return 1
if ( i < 0 )
return 0
if ( j <= 0 and i >= 1 )
return 0
return table[i][j]
end
然后你可以做
table[i][j] = count(table, i, j - 1) + count(table, i - S[j], j)
在你的内循环中。
2)在count2
中,您的第一个循环将发生n - 1
次。这意味着,对于n == 1
,您将不会进入循环体并且找不到任何解决方案。内循环相同:如果只有一枚硬币,你将找不到任何解决方案。
答案 4 :(得分:1)
我相信你的意思是表[i,j]只使用硬币{0,1,2,...,j - 1}来存储实现i美分值的方法的数量。
本质上,while循环的内部部分表示table [i,j]应该等于在不使用任何硬币j的情况下达到i值的方式的数量,加上实现值的方式的数量我使用至少一个价值为S [j]的硬币。因此,除边界情况外,值为table [i,j - 1] + table [i - S [j],j];总和的第一部分是使用没有值为S [j]的硬币到达i的方式的数量,第二部分是使用至少一个值为S [j]的硬币到达i的方式的数量。
尝试更改:
// second sub-problem
// count(n-S[m], m)
if (j - 1 <= -1) // rule 3
total += 0;
else if (i - S[j - 1] == 0) // rule 1
total += 1;
else if (i - S[j - 1] <= -1) // rule 2
total += 0;
else
total += table[i - S[j-1], j];
为:
// second sub-problem
// count(n-S[m], m)
if (i - S[j] == 0) // rule 1
total += 1;
else if (i - S[j] < 0) // rule 2
total += 0;
else
total += table[i - S[j], j];
仅供参考,编写像(j <0)而不是(j <= -1)这样的东西是标准的。