using System; using System.Collections.Generic; using System.Linq; class Program { static void Main(string[] args) { double targetWeight = 23.7; var combinations = CoinCombination.GetCombinations(targetWeight); Console.WriteLine("Target Weight(Gram): " + targetWeight); Console.WriteLine("Number of Combinations: " + combinations.Count); Array.ForEach(Coin.Coins, x => Console.Write((x.Value + "yen").PadLeft(7))); Console.WriteLine(" : Total Amount"); Console.WriteLine(new string('-', 58)); foreach (var c in combinations) { foreach (var coin in Coin.Coins) Console.Write(c.GetCount(coin).ToString().PadLeft(7)); Console.WriteLine(" : " + c.Value.ToString().PadLeft(12)); } } } public struct Coin { public int Value { get; private set; } public double Gram { get; private set; } // Gramの有効数字(小数点以下の桁数) public const int GramDecimals = 1; // 全硬貨のリスト(金額が小さい順) public static readonly Coin[] Coins = new[] { new Coin(1, 1), new Coin(5, 3.7), new Coin(10, 4.5), new Coin(50, 4.0), new Coin(100, 4.8), new Coin(500, 7) }; private Coin(int val, double gram) : this() { Value = val; Gram = Math.Round(gram, GramDecimals); } // 額面が同額以上のコインを返す public IEnumerable GetMoreExpensiveOrEqual() { int index = Array.IndexOf(Coins, this); return Coins.Skip(index); } } public class CoinCombination { public List Coins { get; private set; } // この組み合わせの中の最高額の硬貨。(GetExpandeds参照) public Coin MostExpensive { get { return Coins[Coins.Count - 1]; } } private double mGram; public double Gram { get { return mGram; } private set { mGram = Math.Round(value, Coin.GramDecimals); } } private int mValue = 0; public int Value { get { if (mValue == 0) mValue = Coins.Sum((x) => x.Value); return mValue; } } private CoinCombination() { Coins = new List(); } public CoinCombination(params Coin[] coins) :this() { Coins.AddRange(coins); Gram = Coins.Sum(x => x.Gram); } // コインを一つ追加したCoinCombinationのリストを返す。 // MostExpensiveより額面が小さいコインは追加しない。追加後のGramがmaxGramを超えるCoinCombinationは戻り値に含めない。 private List GetExpandeds(double maxGram) { var ret = new List(); foreach (var coin in MostExpensive.GetMoreExpensiveOrEqual()) { var expanded = new CoinCombination() { Gram = this.Gram + coin.Gram }; if(expanded.Gram <= maxGram) { expanded.Coins.AddRange(this.Coins); expanded.Coins.Add(coin); ret.Add(expanded); } } return ret; } // combinationsの要素とそれらに再帰的にコインを追加したものから // Gramの値がtargetGramに一致するものを検索しリストにして返す。 private static List GetCombinations(double targetGram, IEnumerable combinations) { var ret = new List(); foreach (var c in combinations) { if (c.Gram == targetGram) ret.Add(c); else ret.AddRange(GetCombinations(targetGram, c.GetExpandeds(targetGram))); } return ret; } public static List GetCombinations(double targetGram) { return GetCombinations(targetGram, Coin.Coins.Select(x => new CoinCombination(x))); } public int GetCount(Coin coin) { return Coins.Count(x => x.Value == coin.Value); } }