C#LeetCode刷題之#441-排列硬幣(Arranging Coins)
阿新 • • 發佈:2018-12-13
問題
你總共有 n 枚硬幣,你需要將它們擺成一個階梯形狀,第 k 行就必須正好有 k 枚硬幣。
給定一個數字 n,找出可形成完整階梯行的總行數。
n 是一個非負整數,並且在32位有符號整型的範圍內。
n = 5
硬幣可排列成以下幾行: ¤ ¤ ¤ ¤ ¤
因為第三行不完整,所以返回2.
n = 8
硬幣可排列成以下幾行: ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤
因為第四行不完整,所以返回3.
You have a total of n coins that you want to form in a staircase shape, where every k-th row must have exactly k coins.
Given n, find the total number of full staircase rows that can be formed.
n is a non-negative integer and fits within the range of a 32-bit signed integer.
n = 5
The coins can form the following rows: ¤ ¤ ¤ ¤ ¤
Because the 3rd row is incomplete, we return 2.
n = 8
The coins can form the following rows: ¤ ¤ ¤ ¤ ¤ ¤ ¤ ¤
Because the 4th row is incomplete, we return 3.
示例
public class Program { public static void Main(string[] args) { var n = 8; var res = ArrangeCoins(n); Console.WriteLine(res); n = 18; res = ArrangeCoins2(n); Console.WriteLine(res); n = 28; res = ArrangeCoins3(n); Console.WriteLine(res); n = 38; res = ArrangeCoins4(n); Console.WriteLine(res); n = 68; res = ArrangeCoins5(n); Console.WriteLine(res); Console.ReadKey(); } private static int ArrangeCoins(int n) { //暴力解法,此解法LeetCode超時未AC var res = 0; var value = (long)0; while(n > (value = ComputerT(++res))) { } if(n != value) res--; return res; //以下解法同理,但 LeetCode 可以 AC //if(n == 0) return 0; //for(long i = 1; ; i++) { // if(n >= (i + 1) * i / 2 && n < (i + 2) * (i + 1) / 2) // return (int)i; //} } private static int ComputerT(int n) { //抽出來是為了讓程式結構更清晰 return (1 + n) * n / 2; } private static int ArrangeCoins2(int n) { //按1、2、3、4、5這樣一直減下去 //直到n小於或等於0時為止 var index = 1; while((n -= index++) > 0) { } //考慮上述迴圈中因為是 index++ //index最終被多加了一次 //真實的索引應該是 index - 1 index--; //小於0時,最後一排不完整,所以再減1 if(n < 0) return index - 1; //等於0時,最後一徘正好完整 return index; } private static int ArrangeCoins3(int n) { /* 推導過程 * * 說明=>x ^ 2 表示 x 的平方 * 公式1:a ^ 2 + 2 * a * b + b ^ 2 = (a + b) ^ 2 * 公式2:公差為1的等差數列的求和公式為 S = (1 + n) * n / 2 * * 推導=> * 設在第 k 行硬幣排完,那麼 * (1 + k) * k / 2 = n * 展開並兩邊同時乘以 2 * k + k ^ 2 = 2 * n * 兩邊同時 + 0.25 * k ^ 2 + k + 0.25 = 2 * n + 0.25 * 合併 * (k + 0.5) ^ 2 = 2 * n + 0.25 * 兩邊同時開根號 * k + 0.5 = Math.Sqrt(2 * n + 0.25) * 兩邊同時 - 0.5,求出 k 的值 * k = Math.Sqrt(2 * n + 0.25) - 0.5 */ //原來的 n 為int,直接 2 * n + 0.25 會導致值溢位 //所以用 (long)n 強轉 return (int)(Math.Sqrt(2 * (long)n + 0.25) - 0.5); //以下解法同理,但換了一種求 k 的方式 //具體推導過程請看以下博文 //https://blog.csdn.net/zx2015216856/article/details/81950377 //return (int)((Math.Sqrt(8 * (long)n + 1) - 1) / 2); } private static int ArrangeCoins4(int n) { //解法思路同ArrangeCoins //都屬於暴力求解,但此解法效率略高,可AC var x = (long)n; var temp = (long)n; while(x * x + x > 2 * temp) { x = (x * x + 2 * temp) / (2 * x + 1); } return (int)x; } private static int ArrangeCoins5(int n) { //基本思路同ArrangeCoins和ArrangeCoins4 var k = (int)(Math.Sqrt(2) * Math.Sqrt(n)); return k * (k + 1) <= 2 * n ? k : k - 1; } }
以上給出5種演算法實現,以下是這個案例的輸出結果:
3
5
7
8
11
分析:
顯而易見,以上2種演算法的時間複雜度均為: 。