鴿兔同校

題目描述

  浙江大學校園裏綠樹成蔭,環境很是溫馨,所以也引來一批動物朋友來此居住。
  童心未泯的NowCoder就常常帶些碎麪包什麼的去廣場喂鴿子和兔子,並和它們玩耍。 一點也沒有大學生的樣子,仍是一副老不正經的樣子,呵呵。
  隨着鴿子和兔子數目的增多,NowCoder帶的那點食物已經不夠它們瓜分了。爲了能讓本身的好朋友吃的飽飽的, NowCoder決定統計一下有多少隻鴿子和有多少隻兔子,以便帶來足夠的食物。1、2、3、4、五…他開始數了。
  如今,他已經知道有這些鴿子和兔子一共有n個頭和m只腳。請你幫他寫個程序計算一下一共有多少隻鴿子和兔子。java

1.1 輸入描述:

  輸入包括多組數據。
  每行包括2個正整數n和m,n和m可能會很大,超過2^64,但位數不超過100位。算法

1.2 輸出描述:

  每組數據的輸出都只有一行,分別是鴿子的數量和兔子數量。
  若是輸入的測試數據不能求得結果,那確定是NowCoder這個馬大哈數錯了,就輸出「Error」提示他。數組

1.3 輸入例子:

35 94
1 3

 

1.4 輸出例子:

23 12
Error

 

2 解題思路

  假設有x只鴿子,y只兔子,它們的頭和腳的數目分別是m和n。則有:app

 

{x+y=m2x+4y=n⟺{x=4mn2y=n−2m2x∈Ny∈N ide

 

  x和y都是天然數⇒n爲偶數,x=4mn2⇒4mn爲偶數(必定成立,由於n爲偶數),y=n−2m2⇒n−2m爲偶數(必定成立,由於n爲偶數)。同時由於m和n很是大,要使用大整數進行操做。測試

3 算法實現

import java.util.Scanner;


/**
 * Declaration: All Rights Reserved !!!
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
//        Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
        while (scanner.hasNext()) {
            String m = scanner.next();
            String n = scanner.next();
            String[] r = pigeonAndRabbit(m, n);
            if (r == null) {
                System.out.println("Error");
            } else {
                System.out.println(r[0] + " " + r[1]);
            }
        }

        scanner.close();
    }

    /**
     * 計算鴿子和兔子的數目
     *
     * @param ms 鴿子和兔子的頭數
     * @param ns 鴿子和兔子的腳數
     * @return 長度爲2的數符串,分別表示鴿子的數量和兔子數量,若是無解就返回null
     */
    private static String[] pigeonAndRabbit(String ms, String ns) {

        int lastN = ns.charAt(ns.length() - 1) - '0';
        // ns爲偶數
        if (lastN % 2 != 0) {
            return null;
        }

        int[] m = getNumber(ms);
        int[] n = getNumber(ns);

        // 鴿子數
        // 4 * m
        int[] x = multiply(m, new int[]{4});

        // 兔子數
        // 2 * m
        int[] y = multiply(m, new int[]{2});

        // 4m >= n && n >= 2m
        if (compare(x, n) >= 0 && compare(n, y) >= 0) {

            // 4m - n
            x = minus(x, n);
            // (4m - n) / 2
            x = divide2(x);

            // n - 2m
            y = minus(n, y);
            // (n - 2m) / 2
            y = divide2(y);

            return new String[]{toNumber(x), toNumber(y)};

        } else {
            return null;
        }
    }

    /**
     * 將整數字符串表示成整數數組
     *
     * @param n 整數字符串
     * @return 整數數組 下標從小到大表示數位的從低到高
     */
    private static int[] getNumber(String n) {
        int[] r = new int[n.length()];
        for (int i = 0; i < r.length; i++) {
            r[i] = n.charAt(n.length() - i - 1) - '0';
        }

        return r;
    }

    /**
     * 兩個數相乘
     *
     * @param m 乘數
     * @param n 乘數
     * @return 結果
     */
    private static int[] multiply(int[] m, int[] n) {

        // 結果最多的位數
        int[] r = new int[m.length + n.length];
        // 來自低位的進位
        int c;

        int t;
        int k;

        for (int i = 0; i < n.length; i++) {
            // 計算n[i]*m

            if (n[i] == 0) {
                continue;
            }

            c = 0;
            for (int j = 0; j < m.length; j++) {
                t = n[i] * m[j] + r[i + j] + c;
                r[i + j] = t % 10;
                c = t / 10;

            }

            // 若是還有進位要繼續處理
            k = i + m.length;
            while (c != 0) {
                t = c + r[k];
                r[k] = t % 10;
                c = t / 10;
                k++;
            }
        }

        return r;
    }

    /**
     * 兩個整數相加
     *
     * @param m 整數
     * @param n 整數
     * @return 結果
     */
    private static int[] add(int[] m, int[] n) {

        // 保證n不小於m
        if (m.length > n.length) {
            int[] t = m;
            m = n;
            n = t;
        }

        // 結果的最大長度
        int[] r = new int[n.length + 1];
        // 來自低位的進位
        int c = 0;

        for (int i = 0; i < m.length; i++) {
            r[i] = m[i] + n[i] + c;
            c = r[i] / 10;
            r[i] %= 10;
        }

        // 計算餘下的部分
        for (int i = m.length; i < n.length; i++) {
            r[i] = n[i] + c;
            c = r[i] / 10;
            r[i] %= 10;
        }

        // 最後還有進位
        if (c != 0) {
            r[r.length - 1] = c;
            return r;
        }
        // 沒有進位
        else {
            int[] ret = new int[r.length - 1];
            System.arraycopy(r, 0, ret, 0, ret.length);
            return ret;
        }
    }

    /**
     * 比較兩個整數是否相等,下標由小到大表示由低位到高位,忽略最高有效位上的前導0
     *
     * @param m 整數
     * @param n 整數
     * @return m > n返回1,m = n返回0,m < n返回-1
     */
    private static int compare(int[] m, int[] n) {

        if (m == null && n == null) {
            return 0;
        }
        // null最小
        if (m == null) {
            return -1;
        }

        if (n == null) {
            return 1;
        }

        int lastM = m.length - 1;
        int lastN = n.length - 1;

        // 找m的最高有效位的位置,至少有一位
        while (lastM >= 1 && m[lastM] == 0) {
            lastM--;
        }
        // 找n的最高有效位的位置,至少有一位
        while (lastN >= 1 && n[lastN] == 0) {
            lastN--;
        }

        // m的數位比n多,說明m比n大
        if (lastM > lastN) {
            return 1;
        }
        // m的數位比n少,說明m比n小
        else if (lastM < lastN) {
            return -1;
        } else {
            // 位數同樣,比較每個數位上的值,從高位到低位進行比較
            for (int i = lastM; i >= 0; i--) {
                if (m[i] > n[i]) {
                    return 1;
                } else if (m[i] < n[i]) {
                    return -1;
                }
            }

            return 0;
        }
    }

    /**
     * 作減法n-m,保證n大於等於m
     *
     * @param n 整數
     * @param m 整數
     * @return 結果
     */
    private static int[] minus(int[] n, int[] m) {

        n = format(n);
        m = format(m);

        int[] r = new int[n.length];

        // 當前位被借位
        int c = 0;
        int t;
        for (int i = 0; i < m.length; i++) {
            t = n[i] - c - m[i];
            // 當前位夠減
            if (t >= 0) {
                r[i] = t;
                // 沒有進行借位
                c = 0;
            }
            // 不夠減
            else {
                r[i] = t + 10;
                // 進行借位
                c = 1;
            }
        }

        // 還有借位
        for (int i = m.length; c != 0 && i < n.length; i++) {
            t = n[i] - c;
            // 當前位夠減
            if (t >= 0) {
                r[i] = t;
                // 沒有進行借位
                c = 0;
            }
            // 不夠減
            else {
                r[i] = t + 10;
                // 進行借位
                c = 1;
            }
        }

        return format(r);
    }

    /**
     * 將整數進行格式化,去掉高位的前導0
     *
     * @param r 整數
     * @return 結果
     */
    private static int[] format(int[] r) {
        int t = r.length - 1;
        // 找最高有效位
        while (t > 0 && r[t] == 0) {
            t--;
        }

        int[] nr = new int[t + 1];
        System.arraycopy(r, 0, nr, 0, nr.length);
        return nr;

    }

    /**
     * 將數n除以2
     *
     * @param n 整數
     * @return 結果
     */
    private static int[] divide2(int[] n) {
        // 結果
        int[] r = new int[n.length];
        // 上一位除以2後的餘數
        int c = 0;
        int t;

        for (int i = n.length - 1; i >= 0; i--) {
            t = c * 10 + n[i];
            r[i] = t / 2;
            c = t % 2;
        }

        return format(r);
    }

    /**
     * 將數組表示的整數轉換成字符串
     *
     * @param r 整數
     * @return 字符串表示的整數
     */
    private static String toNumber(int[] r) {
        if (r == null) {
            return null;
        }

        StringBuilder b = new StringBuilder(r.length);

        for (int i = r.length - 1; i >= 0; i--) {
            b.append(r[i]);
        }

        return b.toString();
    }
}

 

4 測試結果

這裏寫圖片描述

相關文章
相關標籤/搜索