给定数N,找到最小的偶数E,使得E> 1。 ñ

时间:2014-04-08 20:27:40

标签: java algorithm

给定数字N,找到最小的偶数E,使得E> 1。 N和N中的数字相同。

   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;
    }

4 个答案:

答案 0 :(得分:2)

第一个想法是生成下一个排列,直到找到偶数排列。这适用于小输入或偶数排列附近,但对于大输入(例如2135791357913579)而言非常糟糕,其中许多排列必须在单个偶数位移动到位之前“向右”发生。

גלעדברקן建议一个小调整的补丁提供了一个优秀的算法。

  • 与下一个置换算法一样,我们找到索引i和j,其中i <1。 j其中第i位小于第j位。当我们交换这些数字时,这比N更大。
  • 在交换后应用i右侧有偶数的附加约束。
  • 将索引右侧最大的偶数(可能不是您刚刚交换过的那个),将其移到最后。这保证了均匀度。
  • 然后按升序对其余数字进行排序。这提供了可用的最小排列。

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

答案 1 :(得分:1)

尝试在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)

答案 2 :(得分:1)

许多人提出了排列,但对于这个问题,使用位掩码进行动态编程将是另一种解决方案。

对于动态编程,数字位数最多可以为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可以用位掩码值替换(在这里读取位掩码Link),所以我们可以很容易地表示这个递归的状态这个数组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开头

答案 3 :(得分:-1)

结果为“无”的案例:

  1. 从给定数字中找出最小偶数(0,2,4,6,8)。设数字为x。
  2. 取其他数字,按降序排序。我们假设这个子串是S。
  3. E = S + x(此处+表示连接)

    1. 如果在步骤1中找不到偶数,则没有这样的数字。
    2. 如果E,在步骤2之后<= N,那么就没有这样的数字。
    3. 因为只有5个可能的数字可以作为E的最后一个数字放置,所以首先我们考虑它们中的每一个。让当前偶数为e。我们将扫描N以查找是否在N中发生e。 如果在N中没有出现e,请跳过。否则,我们从N中删除1次出现的e并将其添加到E的末尾。让我们假设其余的数字连接到E1。

      如果e&gt; N%10,那么我们需要找到E1的置换,使得E1> = N / 10并且E1是最小的。如果e <= N%10,那么我们需要E1的置换,使得E1> 1。 N / 10和E1是每个这种排列的最小值。因此,问题减少以找到数量E1的排列,其大于或等于E1(基于e的值)和最小值。

      你可以从这里开始解决这个问题,因为它只需要从这里进行一些仔细的编码来解决问题的下一部分。