题目详情
一份银行流水数据,因打印模糊导致部分金额不清楚。
收入、支出、余额满足以下3条规则:
1、收入、支出、余额三列都是数字
2、同一行收入和支出的值不能同时为非零值
3、第N-1行余额(+第N行收入或-第N行支出)=第N行余额
程序语言: java
请按照规则编写算法,修复不清楚的值
输入描述:
输入数据最多25行,每行都包含四个数据,分别是:数据编号,收入、支出、余额,模糊的数据以?表示,
它们之间以;隔开。
以文件结尾。第一组数据为初始数据值,收入、支出、余额数据保留2位小数。
输出描述:
以输入的数据顺序输出修复后的数据。
答题说明
输入样例:
流水记录ID;收入;支出;余额
1;0.00;51.90;1945.45
2;0.00;1000.00;?
输出样例:
流水记录ID;收入;支出;余额
1;0.00;51.90;1945.45
2;0.00;1000.00;945.45
试了30次,无法通过。求大神指点。不多说,代码如下
import java.io.IOException; import java.math.BigDecimal; import java.util.Scanner; public class Main { /** * 标志:代表?,即未知数 */ private static BigDecimal Flag = new BigDecimal(-1); /** * 代表0 */ private static BigDecimal ZERO = new BigDecimal(0); /** * 输入数据最大行数 */ private static int MaxLine = 25; /** * <解析为BigDecimal> * @param input * @return */ private static BigDecimal parse(String input) { if (input.equals("?")) { return Flag; } else if (input.equals("0.00") || input.equals("0")) { return ZERO; } return new BigDecimal(input); } /** * <处理单行信息> * @param line * @return */ private static BigDecimal[] parseLine(String line) { String[] strs = line.split(";"); if( strs.length != 4){ return null; } BigDecimal[] out = null; try{ BigDecimal income = parse(strs[1]); BigDecimal payout = parse(strs[2]); BigDecimal balance = parse(strs[3]); if (income != Flag && income != ZERO && payout == Flag) payout = ZERO; else if (payout != Flag && payout != ZERO && income == Flag) income = ZERO; out = new BigDecimal[] {income, payout, balance}; }catch(Exception e){ return null; } return out; } /** * <格式化输出> * @param f * @return */ public static String show(BigDecimal f) { if (f == Flag) { return "?"; } else if (f == ZERO) { return "0.00"; } else { return f.setScale(2, BigDecimal.ROUND_HALF_UP).toString(); } } /** * <处理当前行及上一行余额已知,且当前行中存在2个未知数情形> * @param datas * @param currentLineBalanceIndex */ private static void doCaseOne(BigDecimal[] datas, int currentLineBalanceIndex) { BigDecimal diff = datas[currentLineBalanceIndex].subtract(datas[currentLineBalanceIndex - 3]); if (diff.signum() == -1) { datas[currentLineBalanceIndex - 1] = diff.negate(); datas[currentLineBalanceIndex - 2] = ZERO; } else { datas[currentLineBalanceIndex - 2] = diff; datas[currentLineBalanceIndex - 1] = ZERO; } } public static void main(String[] args) throws IOException { Scanner cin = new Scanner(System.in); String lineStr = null; BigDecimal[] datas = new BigDecimal[MaxLine * 3 + 1];//存放收入,支出,余额数据,从下标为1开始存 String[] idStrs = new String[MaxLine + 1];//存放每行编号信息,从下标为1开始存 int[] lineMisCount = new int[MaxLine + 1];//存放每行未知数总数,从下标为1开始存,可为0-3之间的值 int lineCount = 1;//已读取行数统计 int dataCount = 1;//已读取数据统计 int misCount = 0;//未知数统计 String firstLineStr = null; while (cin.hasNext() && lineCount < MaxLine + 1) { if (firstLineStr == null) {//表头 firstLineStr = cin.nextLine(); continue; } lineStr = cin.nextLine(); BigDecimal[] d = parseLine(lineStr); if(d == null){ continue; } idStrs[lineCount] = lineStr.substring(0, lineStr.indexOf(";")); int lineMis = 0; for (BigDecimal t : d) { if (t == Flag) { misCount++; lineMis++; } } lineMisCount[lineCount] = lineMis; datas[dataCount++] = d[0]; datas[dataCount++] = d[1]; datas[dataCount++] = d[2]; if (lineCount >= 2 && misCount > 0) {//从第二行起,每读一行,循环去倒推当前行及上一行中的未知数 for (int i = lineCount; i > 1 && misCount > 0; i--) { //==========断层情况开始============ if (lineMisCount[i] == 3) {//当前行中三个数未知,将无法倒推出前面所有行中的数据,直接跳出循环,断层原理 break; } int index = i * 3;//当前行对应的余额下标 if (lineMisCount[i - 1] == 0) {//上一行数据全部已知,则最多只能推出本行的未知数据,断层原理 if (lineMisCount[i] == 1) { if (datas[index] == Flag) {//形如 100.00 0.00 ? 形式 datas[index] = datas[index - 3].add(datas[index - 2].subtract(datas[index - 1])); } else {//形如 ? 0.00 100.00 或 0.00 ? 100.00形式 BigDecimal diff = datas[index].subtract(datas[index - 3]); if (diff.signum() == -1) datas[index - 1] = diff.negate(); else datas[index - 2] = diff; } lineMisCount[i]--; misCount--; } else if (lineMisCount[i] == 2) { if (datas[index] != Flag) {//形如 ? ? 100.00形式 doCaseOne(datas, index); lineMisCount[i] = 0; misCount -= 2; } }//其它 80.00 0.00 100.00 及 ? ? ? 这两种情况不考虑,前一种没有未知数据,后一种情况,根据之前的判断,不可能出现 break; } //==========断层情况结束============ if (lineMisCount[i] == 0) {//当前行数据全部已知 if (datas[index - 3] == Flag) {//推出上一行中的余额 datas[index - 3] = datas[index].add(datas[index - 1]).subtract(datas[index - 2]); lineMisCount[i - 1]--; misCount--; } continue; } if (lineMisCount[i] == 2) {//当前行两个未知数 if (datas[index] != Flag && datas[index - 3] != Flag) {//形如 ? ? 100.00形式 及上一行的 x x 100.00形式 doCaseOne(datas, index); lineMisCount[i] = 0; misCount -= 2; }//其它情况既无法推出本行未知数,也无法推出上一行的未知数 continue; } if (lineMisCount[i] == 1) {//当前行一个未知数 if (datas[index - 3] != Flag) {//只有上一行余额已知时才能推导出本行未知数 if (datas[index - 1] == Flag) { datas[index - 1] = datas[index - 3].subtract(datas[index]); } else if (datas[index - 2] == Flag) { datas[index - 2] = datas[index].subtract(datas[index - 3]); } else { datas[index] = datas[index - 3].add(datas[index - 2]).subtract(datas[index - 1]); } lineMisCount[i] = 0; misCount--; } } } } lineCount++; } lineCount--; int size = lineCount * 3; if (firstLineStr != null) { System.out.println(firstLineStr);//输出表头 } for (int i = 1; i <= size; i += 3) { int lineNum = i / 3 + 1; System.out.println(idStrs[lineNum] + ";" + show(datas[i]) + ";" + show(datas[i + 1]) + ";" + show(datas[i + 2])); } cin.close(); } }
上面用的是BigDecimal来存放数据,个人觉得用double存也是可以的。