第3章在线编程题参考答案
3.1第1章绪论
1. POJ1004——财务管理问题

时间限制: 1000ms; 空间限制: 10000KB。
问题描述: 拉里今年毕业,终于找到了工作。他赚了很多钱,但似乎还没有足够的钱,拉里决定抓住金融投资机会解决他的财务问题。拉里有自己的银行账户报表,他想看看自己有多少钱。请编写一个程序帮助拉里从过去12个月的每一月中取出他的期末账户余额并计算他的平均账户余额。
输入格式: 输入为12行,每行包含特定月份的银行账户的期末余额,每个数字都是正数并到便士为止,不包括美元符号。
输出格式: 输出一个数字,即12个月的期末账户余额的平均值(平均值),它将四舍五入到最近的便士,紧接着是美元符号,然后是行尾。在输出中不会有其他空格或字符。
输入样例: 

100.00

489.12

12454.12

1234.10

823.05

109.20

5.27

1542.25

839.18

83.99

1295.01

1.75


输出样例: 

$1581.42


解: 读取每个月份的期末账户余额temp,累加到sum中,输出sum/12的结果。对应的AC程序如下: 

import java.util.Scanner; 

public class Main

{public static void main(String[] args)

{Scanner fin=new Scanner(System.in);

double temp=0.00,sum=0.00;

int iMax=12;

while(iMax-->0)

{temp=fin.nextDouble();

sum += temp;

}

System.out.printf("$%.2f\n",sum/12);

}

}

上述程序的执行时间为188ms,执行空间为3228KB。

第3章在线编程题参考答
案

数据结构教程(Java语言描述)学习与上机实验指导
2. HDU2012——素数判定问题
时间限制: 2000ms; 空间限制: 65536KB。
问题描述: 对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x、y,-39≤x<y≤50)判定该表达式的值是否都为素数。
输入格式: 输入数据有多组,每组占一行,由两个整数x、y组成,当x=0、y=0时表示输入结束,该行不做处理。
输出格式: 对于每个给定范围内的取值,如果表达式的值都为素数,输出“OK”,否则输出“Sorry”,每组输出占一行。
输入样例: 

0 1

0 0

输出样例: 

OK

解: 对于整数num,若它能够整除2~sqrt(num)中的任何整数,则不是素数,否则为素数。对应的AC程序如下: 

import java.util.Scanner;

public class Main

{public static void main(String[] args)

{Scanner in = new Scanner(System.in);

while(in.hasNextInt())

{int x = in.nextInt();

int y = in.nextInt();

boolean flag = true;

int number = 0;

int num = 0;

if(x==0 && y= 0)

break;

else

{if(x>y)

{int temp = x;

x = y;

y = temp;

}

for(int i = x; i <= y; i++)

{num=i*i+i+41;

for(int j = 2; j <= Math.sqrt(num); j++)//素数判断

{if(num % j == 0)

flag = false;

}

}

if(flag == true)

System.out.println("OK");

else

System.out.println("Sorry");

flag = true;

}

}

}

}

上述程序的执行时间为296ms,执行空间为9312KB。
提示: 在本书所有在线编程题中,POJ题目的限制时间和空间是针对C/C++程序的,通常Java程序的时间和空间是其两倍,HDU题目的限制时间和空间就是针对Java程序的。
3.2第2章线性表
1. HDU2019——数列有序问题

时间限制: 2000ms; 空间限制: 65536KB。
问题描述: 有n(n≤100)个整数,已经按照从小到大的顺序排列好,现在另外给一个整数m,请将该数插入序列中,并使新的序列仍然有序。
输入格式: 输入数据包含多个测试实例,每组数据由两行组成,第一行是n和m,第二行是已经有序的n个数的数列。n和m同时为0表示输入数据的结束,本行不做处理。
输出格式: 对于每个测试实例,输出插入新的元素后的数列。
输入样例: 

3 3

1 2 4

0 0

输出样例: 

1 2 3 4

解: 对于每个测试实例,n个有序整数采用顺序表存放,为了简单,这里顺序表直接用数组a表示。先查找插入m的位置i,将a[i..n-1]的所有元素均后移一个位置,置a[i]=m。对应的AC程序如下: 

import java.util.*;

public class Main

{public static void main(String args[])

{Scanner cin=new Scanner(System.in);

while(cin.hasNext())					  //读取每个测试实例

{int n=cin.nextInt();				  //读取n

int m=cin.nextInt();				  //读取m

if(n==0 && m==0)				  //结束

break;

else

{int a[]=new int[105];			  //最多100个整数

for(int i=0;i<n;i++)			  //读取递增有序整数序列

a[i]=cin.nextInt();

if(m>a[n-1])					  //m最大,插入末尾

a[n]=m;

else

{for(int i=0;i<n;i++)		  //查找第一个大于等于m的元素a[i]

{if(m<a[i])

{int j=i;

for( i=n;i>j;i--)	  //将a[i..n-1]后移一个位置

a[i]=a[i-1];

a[i]=m;			  //插入a[i]

break;			  //退出查找

}

}

}

for(int i=0;i<=n;i++)			  //输出新序列

if(i==0)

System.out.print(a[i]);

else

System.out.print(" "+a[i]);

}

System.out.println();

}

}

}

上述程序的执行时间为265ms,空间为9412KB。
2. HDU1443——Joseph(约瑟夫)问题
时间限制: 2000ms; 空间限制: 65536KB。
问题描述: Joseph问题是众所周知的。有n个人,编号为1,2,…,n,站在一个圆圈中,每隔m个人就杀一个人,最后仅剩下一个人。Joseph很聪明,可以选择最后一个人的位置,从而挽救他的生命。例如,当n=6且m=5时,按顺序出列的人员是5,4,6,2,3,1,那么1会活下来。
假设在圈子里前面恰好有k个好人,后面恰好有k个坏人,则必须确定所有坏人都在第一个好人前面被杀的最小m。
输入格式: 输入文件中包含若干行,每行一个k,最后一行为0,可以假设0<k<14。
输出格式: 输出文件中每行给出输入文件中的k对应的最小m。
输入样例: 

3

4

0

输出样例: 

5

30

解: 题目中有多个测试实例,每个测试实例的结果仅仅与k有关系,设计避免重复计算的数组a,a[k]记录k问题的m值,a数组的所有元素初始为0,当a[k]不为0时说明该k问题已经求出,直接返回a[k]即可。
对于每个k问题,假设2k个人的序号是0~2k-1,显然m从k+1开始枚举(因为每次从头开始,若m<k+1,则第一个被杀的一定是好人)。对于每个m: 
(1) cnt表示剩余人数,所以cnt从2k开始到k+1循环,p从0开始找被杀的人员的序号,每杀一个人,cnt减少1。
(2) 若被杀人员的序号p满足p<k(前k个好人的序号为0~k-1),说明p对应的是好人,置cnt=0结束该m值的枚举。
(3) 若cnt==k,则说明剩下k个人,并且被杀的k个人都是坏人,则找到这样的m,置a[k]=m,返回m。
对应的AC程序如下: 

import java.util.*;

public class Main

{static int[] ans=new int[15];				  //记录k问题的m值,初始时均为0

public static int Joseph(int k)			  //求解Joseph问题

{int cnt,p;

if(ans[k]!=0) return ans[k];			  //若已经求出,直接返回

for(int m=k+1;;m++)					  //m从k+1开始试探

{for(cnt=2*k,p=0;cnt>k;cnt--)		  //检查是否满足要求

{p=(p+m-1) % cnt;			  //被杀掉的人的位置

if(p<k) cnt=0;				  //被杀掉的人是前k个(好人)

}

if(cnt==k)						  //所有坏人都被杀了,满足要求

{ans[k]=m;

return m;

}

}

}

public static void main(String args[])

{Scanner cin=new Scanner(System.in);

while(cin.hasNext())

{int k=cin.nextInt();

if(k==0) break;

System.out.println(Joseph(k));

}

}

}

上述程序的执行时间为577ms,空间是10388KB。
3. POJ2389——公牛数学问题
时间限制: 1000ms; 空间限制: 65536KB。
问题描述: 公牛在数学上比奶牛好多了,它们可以将巨大的整数相乘,得到完全精确的答案。农夫约翰想知道它们的答案是否正确,请帮助他检查公牛队的答案。读入两个正整数(每个不超过40位)并计算其结果,以常规方式输出(没有额外的前导零)。约翰要求不使用特殊的库函数进行乘法。
输入格式: 输入两行,每行包含一个十进制数。
输出格式: 输出一行表示乘积。
输入样例: 

11111111111111

1111111111

输出样例: 

12345679011110987654321

解: 两个长度最多为40位的正整数采用String对象s1、s2表示。设置a、b两个整数数组(初始时所有元素为0),将s1的各位存放在a中(s1的最后位存放在a[0]中),将s2的各位存放在b中(s2的最后位存放在b[0]中)。
实现a与b相乘的过程是用c数组的c[i+j]元素存放a[i]和b[j]相乘的累加值,然后调整有进位的元素,反向输出得到最后结果。
对应的AC程序如下: 

import java.lang.*;

import java.util.*;

public class Main

{public static String mult(String s1,String s2)  //两个数字字符串相乘

{int[] a=new int[42];

int[] b=new int[42];

int[] c=new int[90];

int i,j;

int m=s1.length();

int n=s2.length();

for(i=0;i<m; i++)					  //a[0]存放s1的最低位

a[i]=s1.charAt(m-1-i)-'0';

for(i=0;i<n;i++)						  //b[0]存放s2的最低位

b[i]=s2.charAt(n-1-i)-'0';

for(i=0;i<m;i++)

for(j=0; j<n; j++)

c[i+j] += a[i]*b[j];

for(i=0;i<80;i++)

if(c[i] >= 10)					  //有进位

{c[i+1] += c[i]/10;

c[i] %= 10;

}

String ans="";

for(i=c.length-1;i>=0; i--)			  //删除前导零

if(c[i]!=0) break;

for(;i>=0; i--)

ans += c[i];

return ans;

}

public static void main(String[] args)

{Scanner cin = new Scanner(System.in);

String s1,s2;

s1=cin.nextLine();

s2=cin.nextLine();

String s3=mult(s1,s2);

System.out.println(s3);

}

}

上述程序的执行时间为1266ms,空间是2964KB。实际上,本题目可以直接使用Java的长整数类型来实现,对应的AC程序如下: 


import java.math.BigInteger;

import java.util.Scanner;

public class Main

{public static void main(String[] args)

{Scanner cin = new Scanner(System.in);

BigInteger a = cin.nextBigInteger();

BigInteger b = cin.nextBigInteger();

a = a.multiply(b);

System.out.println(a);

}

}

上述程序的执行时间为1375ms,空间是3036KB。
说明: 尽管在线性表部分中讨论了多种存储结构,但在实际在线编程中,为了简单和提高效率往往直接采用数组代替顺序表,相反链表较少使用,其原因之一是在这类题目中都确定了数据量的大小,适合采用顺序结构,另外链表由于结点地址不连续,内存寻址空间较大,导致存取效率较低。
3.3第3章栈和队列
1. HDU1237——简单计算器

时间限制: 2000ms; 空间限制: 65536KB。
问题描述: 读入一个只包含+、-、*、/的非负整数计算的表达式,计算该表达式的值。
输入格式: 测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔; 没有非法表达式; 当一行中只有0时输入结束,相应的结果不要输出。
输出格式: 对每个测试用例输出一行,即该表达式的值,精确到小数点后两位。
输入样例: 

1 + 2

4 + 2 * 5 - 7 / 11

0

输出样例: 

3.00

13.36

解: 与《教程》中3.1.7节“用栈求解简单表达式求值问题”的原理相同,由于这里不包括括号,所以更简单一些,将求表达式值的两个步骤合并起来。
设计运算数栈为opand,运算符栈为oper。先将输入行str拆分为strs字符串数组,用i遍历strs: 
(1) 若strs[i]为“*”或者“/”,为最高优先级,将oper栈顶为“*”或者“/”的运算符先计算,再将strs[i]进oper栈。
(2) 若strs[i]为“+”或者“-”,为最低优先级,将oper栈中所有的运算符先计算,再将strs[i]进oper栈。
(3) 其他为运算数,将其进opand栈。
最后输出opand的栈顶数值。
对应的AC程序如下: 

import java.util.*;

import java.util.Scanner;

public class Main

{static Stack<Double> opand=new Stack<Double>();	  //运算数栈

static Stack<String> oper=new Stack<String>();		  //运算符栈

public static boolean Same(String s1,String s2)		  //比较两个字符串值是否相同

{

return s1.compareTo(s2)==0;

}

public static void comp(String op)					  //用于一个运算符的计算

{double a=opand.pop();							  //从opand中出栈两个数a和b

double b=opand.pop();

double c=0.0;

if(Same(op,"+"))								  //op为“+”

c=b+a;

else if(Same(op,"-"))							  //op为“-”

c=b-a;

else if(Same(op,"*"))							  //op为“*”

c=b*a;

else if(Same(op,"/"))								  //op为“/”

c=b/a;

opand.push(c);									  //结果进opand栈

}

public static void main(String[] args)

{Scanner fin = new Scanner(System.in);

while(fin.hasNext())

{String str=fin.nextLine();						  //接收输入的字符串

if(str.compareTo("0")==0) 					  //若是只输入0,则结束

break;

opand.clear();

oper.clear();

String[] strs=str.split(" ");						  //根据题目要求分割字符串

String op;

double a,b,c;

int i=0;

while(i<strs.length)							  //遍历strs

{if(Same(strs[i],"*") || Same(strs[i],"/"))	  //遇到*或者/

{while(!oper.empty())

{op=oper.peek();

if(Same(op,"*") || Same(op,"/")) 	  //栈顶为*或者/,先计算

{comp(op);

oper.pop();

}

else break;						  //其他+或者-

}

oper.push(strs[i]);					  //将strs[i]进oper栈

}

else if(Same(strs[i],"+") || Same(strs[i],"-"))  //遇到+或者-

{while(!oper.empty())					  //计算oper栈中所有的运算符

{op=oper.pop();

comp(op);

}

oper.push(strs[i]);					  //将strs[i]进oper栈

}

else										  //其他运算数进opand栈

opand.push(Double.parseDouble(strs[i])); 

i++;

}

while(!oper.empty())							  //计算oper栈中所有的运算符

{op=oper.pop();

comp(op);

}

System.out.printf("%.2f", opand.peek());

System.out.println();

}

}

}

上述程序的执行时间为452ms,空间为13620KB。
说明: 类似的题目有POJ3295。
2. POJ1363——铁轨问题
时间限制: 1000ms; 空间限制: 65536KB。
问题描述: A市有一个著名的火车站,那里的山非常多。该站建于20世纪,不幸的是该火车站是一个死胡同,并且只有一条铁轨,如图3.1所示。
当地的传统是从A方向到达的每列火车都继续沿B方向行驶,需要以某种方式进行车厢重组。假设从A方向到达的火车有n(n≤1000)个车厢,按照递增的顺序1、2、…、n编号。火车站负责人必须知道是否可以通过重组得到B方向的车厢序列。


图3.1铁轨问题示意图


输入格式: 输入由若干行块组成; 除了最后一个之外,每个块描述了一个列车以及可能更多的车厢重组要求; 在每个块的第一行中为上述的整数n,下一行是1、2、…、n的车厢重组序列; 每个块的最后一行仅包含0; 最后一个块只包含一行0。
输出格式: 输出包含与输入中具有车厢重组序列对应的行。如果可以得到车厢对应的重组序列,输出一行“Yes”,否则输出一行“No”。此外,在输入的每个块之后有一个空行。
输入样例: 

5

1 2 3 4 5

5 4 1 2 3

0

6

6 5 4 3 2 1

0

0

输出样例: 

Yes

No



Yes