版權聲明:本文爲博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/kexuanxiu1163/article/details/99024362
題目描述
給你數字 0 ,1 ,2 ,那麼所有排列從小到大就會是 012,021,102,120,201,210,那麼如果給你 0,1,2,3,4,5,6,7,8,9 讓你去排,從小到大排在第一百萬的數是多少?
題目地址:https://projecteuler.net/problem=24
題目解析
寫普通算法程序題寫多了,見到 排列 首當其衝想到用深度優先搜索,最後發現如果把所有的排列可能都給列出來,然後排序,這樣搞下來程序運行的時間複雜度是 O(n!),比指數的時間複雜度還要高!
如果從數學思路上去思考的話,那問題就簡單了!
通過一個數一個數的去選。
首先第一個位置(最高位)有十種可能性,我們可以利用 10! / 10 來計算出每一個數字打頭有多少種排列,然後用 一百萬 / (10! / 10) 來決定我們要選排在第幾的數,選中一個數後,剩下的數就只有 9 個,將 10 換成 9,重複上面的操作,當然,一百萬這個值也得根據我們找的數來做更新,比如你第一個數選擇了 2,那麼 0 和 1 打頭的 2 * 10! / 10 種組合都可以減掉,剩下的我們需要繼續在後面的 9 個數的排列中找。
//@author:P.yhpublic static void main(String[] args) { System.out.println(lexicographicPermu(10, 1000000L));}private static String lexicographicPermu(int numberOfDigit, long target) { int copyOfNumerOfDigit = numberOfDigit; long copyOfTarget = target; // list 裏面存放的是所有的可以選擇的情況,並從大到小排列 // 這裏是 0,1,2,3,4,5,6,7,8,9 List<Integer> candidate = new ArrayList<>(); for (int i = 0; i < copyOfNumerOfDigit; ++i) { candidate.add(i); } String result = ""; // 只要沒有選完,就繼續選擇 while (copyOfNumerOfDigit != 0) { long totalPermutation = 1; int n = copyOfNumerOfDigit; // 計算 n! // 例如,選擇第一個數,那 totalPermutation 就是 10! while (n != 1) { totalPermutation *= n; n--; } // 計算選擇當前情況下的第幾個數 // 這裏 copyOfTarget - 1 是爲了防止整除的情況 // 舉 0,1,2 這個例子,當我們要選擇排在第 2 的那個數,也就是 021 // 選第一個數的時候, totalPermutation = 3 * 2 * 1 = 6, copyOfNumberOfDigit = 3 // 2 / (6 / 3) = 1,顯然不符合要求,(2 - 1) / (6 / 3) = 0 纔是選中了 0 int selection = (int)((copyOfTarget - 1) / (totalPermutation / copyOfNumerOfDigit)); result += candidate.remove(selection); // 更新下一次要選擇第幾個數 copyOfTarget -= (totalPermutation / copyOfNumerOfDigit) * selection; // 選了一個數,可選項相應減少一 copyOfNumerOfDigit--; copyOfTarget = copyOfTarget <= 0 ? 0 : copyOfTarget; } return result;}
public static void main(String[] args) {
System.out.println(lexicographicPermu(10, 1000000L));
}
private static String lexicographicPermu(int numberOfDigit, long target) {
int copyOfNumerOfDigit = numberOfDigit;
long copyOfTarget = target;
// list 裏面存放的是所有的可以選擇的情況,並從大到小排列
// 這裏是 0,1,2,3,4,5,6,7,8,9
List<Integer> candidate = new ArrayList<>();
for (int i = 0; i < copyOfNumerOfDigit; ++i) {
candidate.add(i);
}
String result = "";
// 只要沒有選完,就繼續選擇
while (copyOfNumerOfDigit != 0) {
long totalPermutation = 1;
int n = copyOfNumerOfDigit;
// 計算 n!
// 例如,選擇第一個數,那 totalPermutation 就是 10!
while (n != 1) {
totalPermutation *= n;
n--;
}
// 計算選擇當前情況下的第幾個數
// 這裏 copyOfTarget - 1 是爲了防止整除的情況
// 舉 0,1,2 這個例子,當我們要選擇排在第 2 的那個數,也就是 021
// 選第一個數的時候, totalPermutation = 3 * 2 * 1 = 6, copyOfNumberOfDigit = 3
// 2 / (6 / 3) = 1,顯然不符合要求,(2 - 1) / (6 / 3) = 0 纔是選中了 0
int selection = (int)((copyOfTarget - 1) / (totalPermutation / copyOfNumerOfDigit));
result += candidate.remove(selection);
// 更新下一次要選擇第幾個數
copyOfTarget -= (totalPermutation / copyOfNumerOfDigit) * selection;
// 選了一個數,可選項相應減少一
copyOfNumerOfDigit--;
copyOfTarget = copyOfTarget <= 0 ? 0 : copyOfTarget;
}
return result;
}
---------------------
版權聲明:本文爲CSDN博主「程序員吳師兄」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/kexuanxiu1163/article/details/99024362