题目详情
一份银行流水数据,因打印模糊导致部分金额不清楚。
收入、支出、余额满足以下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存也是可以的。