[英]Program to find smallest permutation of a number n1 but should be larger than another given number n2; print “Invalid” if not possible
[英]Given a number N, find the smallest even number E such that E > N
給定數字N,找到最小的偶數E,使得E> N並且N和E中的數字相同。
Print NONE otherwise.
Sample:
Case1
Input
N = 34722641
Output
E = 34724126
Case2
Input
N = 8234961
Output
E = 8236194 (instead of 8236149)
我的第二個案例通過了第一個案例我得錯了輸出
public static int nextDigit(int number) {
String num = String.valueOf(number);
int stop = 0;
char[] orig_chars = null;
char[] part1 = null;
char[] part2 = null;
orig_chars = num.toCharArray();
for (int i = orig_chars.length - 1; i > 0; i--) {
String previous = orig_chars[i - 1] + "";
String next = orig_chars[i] + "";
if (Integer.parseInt(previous) < Integer.parseInt(next))
{
if (Integer.parseInt(previous) % 2 == 0) {
String partString1 = "";
String partString2 = "";
for (int j = 0; j <= i - 1; j++) {
partString1 = partString1.concat(orig_chars[j] + "");
}
part1 = partString1.toCharArray();
for (int k = i; k < orig_chars.length; k++) {
partString2 = partString2.concat(orig_chars[k] + "");
}
part2 = partString2.toCharArray();
Arrays.sort(part2);
for (int l = 0; l < part2.length; l++) {
char temp = '0';
if (part2[l] > part1[i - 1]) {
temp = part1[i - 1];
part1[i - 1] = part2[l];
part2[l] = temp;
break;
}
}
for (int m = 0; m < part2.length; m++) {
char replace = '0';
if (part2[m] % 2 == 0) {
replace = part2[m];
for (int n = m; n < part2.length - 1; n++) {
part2[n] = part2[n + 1];
}
part2[part2.length - 1] = replace;
break;
}
}
System.out.print(part1);
System.out.println(part2);
System.exit(0);
}
}
}
System.out.println("NONE");
return 0;
}
第一個想法是生成下一個排列直到找到偶數排列。 這適用於小輸入或偶數排列附近,但對於大輸入(例如2135791357913579)非常有效,其中許多排列必須在單個偶數位移動到位之前“向右”發生。
גלעדברקן建議一個小調整的補丁提供了一個優秀的算法。
Clojure中的算法可以寫成如下。 我試圖堅持一種相當勢在必行的風格。 看看你是否可以翻譯。
(defn num->digits [n] (mapv #(Character/getNumericValue %) (str n)))
(defn digits->num [v] (when (seq v) (read-string (apply str v))))
(defn swap
"Swap elements at index i and j in vector v"
[v i j]
(assoc (assoc v i (v j)) j (v i)))
(defn find-max-where
"Find index i in vector v such that (v i) is the largest satisfying pred"
[pred v]
(first
(reduce-kv
(fn [[k m] i x]
(if (and m (> m x))
[k m]
(if (pred x) [i x] [k m])))
[nil nil]
v)))
(defn next-even-perm [v]
(->>
(for [j (range (count v))
i (range j)
:when (< (v i) (v j))
:let [v (swap v i j)
k (find-max-where even? (vec (subvec v (inc i))))]
:when k
:let [v (swap v (+ (inc i) k) (dec (count v)))]]
(concat (subvec v 0 (inc i))
(sort (subvec v (inc i) (dec (count v))))
[(peek v)]))
(map vec) sort first))
(defn next-even-num [n] (-> n num->digits next-even-perm digits->num))
提供的示例:
(next-even-num 34722641)
;=> 34724126
(next-even-num 8234961)
;=> 8236194
(next-even-num 4321)
;=> nil (no solution)
以前的算法的硬案例
(time (next-even-num 2135791357913579))
; "Elapsed time: 1.598446 msecs"
;=> 3111335557779992
(time (next-even-num 13244351359135913))
; "Elapsed time: 1.713501 msecs"
;=> 13245111333355994
(time (next-even-num 249999977777555553333311111N))
; "Elapsed time: 1.874579 msecs"
;=> 251111133333555577777999994N
最新的編輯修復問題,我們總是希望與偶數移動權交換,而不是只有任何偶數向右,無論它是否涉及交換。 例如,以前編輯中的以下內容失敗,現在已修復。
(next-even-num 1358)
;=> 1538
試圖在A. Webb的答案中使用Haskell版本的新算法:
import qualified Data.Map as M
import Data.Ord (comparing)
import Data.List (sort,maximumBy)
import Data.Maybe (fromJust)
digs :: Integral x => x -> [x]
digs 0 = []
digs x = digs (x `div` 10) ++ [x `mod` 10]
nextE n
| e == -1 = "NONE"
| otherwise = concatMap show $ take (ie' + 1) (M.elems s)
++ sort (drop (ie' + 1) (M.elems s')) ++ [r]
where ds = M.fromList (zip [0..] (digs n))
rightMost (-1) _ = [(-1,0),(-1,0)]
rightMost ix xs
| even (x) && not (null $ filter (>x) xs) = [(x,ix),(y,iy)]
| otherwise = rightMost (ix - 1) (x:xs)
where x = fromJust (M.lookup ix ds)
(y,iy) = minimum . filter ((>= x) . fst)
$ zip xs [ix + 1..M.size ds - 1]
[(e,ie),(l,il)] = rightMost (M.size ds - 1) []
s = M.insert il e . M.insert ie l $ ds
(ir,r) = maximumBy (comparing snd) . M.toList
. M.filter even . snd $ M.split ie s
s' = M.delete ir s
ie' = fromIntegral ie
main = print (map (\x -> (x,nextE x)) [34722641,8234961,13244351359135913,3579])
輸出:
*Main> main
[(34722641,"34724126"),(8234961,"8236194")
,(13244351359135913,"13245111333355994"),(3579,"NONE")]
(0.02 secs, 563244 bytes)
許多人提出了排列,但對於這個問題, 使用位掩碼的動態編程將是另一種解決方案。
對於動態編程,數字位數最多可以為20位,而正常排列時,只有在N小於12位時才能使用。 (時間約束為1秒,通常用於競爭性編程)
所以我們的想法是,從最重要的數字到最低有效數字,在每一步,我們都試圖找到從這個數字到結尾的最小值,在每個數字上,我們有兩種情況:
如果創建的數字已經大於N,例如N是12345,目前我們是23xxx。 (在這種情況下,我們需要找到最小的xxx)。
如果數字還不大於N,則示例N是12345,我們有12xxx。
此外,在最后一位,我們需要確定創建的數字是偶數還是奇數。
所以,我們有簡單的遞歸代碼:
public int cal(boolean[] selected, int[] num, int digit, boolean larger) {
//Arrays selected will tell which digit in N has already selected,
//int digit will tell currently, which digit we are checking
//boolean larger tells is the number already larger than N
if (digit + 1 == selected.length) {//Last digit
for (int i = 0; i < selected.length; i++) {
if (!selected[i]) {
if (num[i] % 2 != 0) {
return -1; // -1 means this is an invalid value
} else {
if (larger) {
return num[i];
} else {
return -1;
}
}
}
}
}
int result = -1;
for (int i = 0; i < selected.length; i++) {
if (!selected[i]) {
if (larger) {
selected[i] = true;
int val = (int) (num[i] * Math.pow(10, digit) + cal(selected, num, digit + 1, larger));
if (val != -1 && (result == -1 || result > val)) {
result = val;
}
} else if (num[i] >= num[digit]) {
int val = (int) (num[i] * Math.pow(10, digit) + cal(selected, num, digit + 1, num[i] > num[digit]));
if (val != -1 && (result == -1 || result > val)) {
result = val;
}
}
}
}
return result;
}
從這個狀態,我們注意到實際上,所boolean [] selected
的boolean [] selected
可以用位掩碼值替換(在這里讀取有關位掩碼的鏈接 ),所以我們可以通過這個數組int [mask][larger] dp
輕松地表示這個遞歸的狀態。 int [mask][larger] dp
請注意,參數digit
不是必需的,因為我們可以通過計算要選擇的數字位數來輕松確定我們正在檢查的數字。
最后,我們有解決方案:
import java.util.Arrays;
/**
*
* @author Trung Pham
*/
public class Test {
public static void main(String[] args) {
Test test = new Test();
System.out.println(test.largest(2135791357913579L));
}
long[][] dp;
public long largest(long N) {
String val = "" + N;
int[] num = new int[val.length()];
for (int i = 0; i < num.length; i++) {
num[i] = val.charAt(i) - '0';
// System.out.println(num[i] + " " + i);
}
dp = new long[1 << num.length][2];
for (long[] a : dp) {
Arrays.fill(a, -2);
}
return cal(0, num, 0);
}
public long cal(int mask, int[] num, int larger) {
//Arrays selected will tell which digit in N has already selected,
//int digit will tell currently, which digit we are checking
//int larger tells is the number already larger than N, if it is 1, it is larger, 0 is not.
int digit = 0;
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) != 0) {
digit++;
}
}
if (dp[mask][larger] != -2) {
return dp[mask][larger];
}
if (digit + 1 == num.length) {//Last digit
//System.out.println(mask + " " + digit);
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) == 0) {
if (num[i] % 2 != 0) {
return -1; // -1 means this is an invalid value
} else {
if (larger == 1) {
// System.out.println(num[i] + " " + i);
return num[i];
} else {
return -1;
}
}
}
}
return -1;
}
long result = -1;
int l = num.length;
for (int i = 0; i < num.length; i++) {
if (((1 << i) & mask) == 0) {
if (larger == 1) {
//System.out.println(num[i]* Math.pow(10,l - digit) + " " + digit);
long val = (long) (cal(mask | (1 << i), num, larger));
if (val != -1) {
val += num[i] * Math.pow(10, l - digit - 1);
if (result == -1 || result > val) {
result = val;
}
}
} else if (num[i] >= num[digit]) {
long val = (long) (cal(mask | (1 << i), num, num[i] > num[digit] ? 1 : 0));
if (val != -1) {
val += num[i] * Math.pow(10, l - digit - 1);
if (result == -1 || result > val) {
result = val;
}
}
}
}
}
return dp[mask][larger] = result;
}
}
請注意,如果您注意到在每個數字處,我們僅選擇0到9之間的值 ,並且起始數字不能以0開頭,則仍可以改進此解決方案。
結果為“無”的情況:
E = S + x(此處+表示連接)
因為只有5個可能的數字可以作為E的最后一個數字放置,所以首先我們考慮它們中的每一個。 讓當前偶數為e。 我們將掃描N以查找是否在N中出現e。如果在N中沒有出現e,則跳過。 否則,我們從N中刪除1次出現的e並將其添加到E的末尾。讓我們假設其余的數字連接到E1。
如果e> N%10,那么我們需要找到E1的置換,使得E1> = N / 10並且E1是最小的。 如果e <= N%10,那么我們需要E1的置換,使得E1> N / 10和E1是每個這種置換的最小值。 因此,問題減少以找到數量E1的排列,其大於或等於E1(基於e的值)和最小值。
您可以從這里開始解決這個問題,因為它只需要從這里進行一些仔細的編碼來解決問題的下一部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.