题目描述
浙江大学校园里绿树成荫,环境非常舒适,因此也引来一批动物朋友来此居住。
童心未泯的NowCoder就经常带些碎面包什么的去广场喂鸽子和兔子,并和它们玩耍。 一点也没有大学生的样子,还是一副老不正经的样子,呵呵。
随着鸽子和兔子数目的增多,NowCoder带的那点食物已经不够它们瓜分了。为了能让自己的好朋友吃的饱饱的, NowCoder决定统计一下有多少只鸽子和有多少只兔子,以便带来足够的食物。一、二、三、四、五…他开始数了。
现在,他已经知道有这些鸽子和兔子一共有n个头和m只脚。请你帮他写个程序计算一下一共有多少只鸽子和兔子。
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。则有:
{x+y=m2x+4y=n⟺{x=4m−n2y=n−2m2x∈Ny∈N
x和y都是自然数⇒n为偶数,x=4m−n2⇒4m−n为偶数(一定成立,因为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();
}
}