描述:
小明同学最近新购置了电脑,想和其他同学玩CS,但是建了局域网游戏之后,别人加不进来,自己也进不了别人的主机,非常苦恼。于是来请教同宿舍的“科技怪人”小犀,小犀说了句“你的IP和我们不在同一个子网”就闪了,小明百度了一下,搜到如下关于“子网掩码”的信息:子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据。最为简单的理解就是两台计算机各自的IP地址与子网掩码进行AND运算后,如果得出的结果是相同的,则说明这两台计算机是处于同一个子网里面,可以进行直接的通讯。
运算演示之一:
I P 地址 192.168.0.1
子网掩码 255.255.255.0
转化为二进制进行运算:
I P 地址 11010000.10101000.00000000.00000001
子网掩码 11111111.11111111.11111111.00000000
AND运算
11010000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0
运算演示之二:
I P 地址 192.168.0.254
子网掩码 255.255.255.0
转化为二进制进行运算:
I P 地址 11010000.10101000.00000000.11111110
子网掩码 11111111.11111111.11111111.00000000
AND运算
11010000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0
运算演示之三:
I P 地址 192.168.0.4
子网掩码 255.255.255.0
转化为二进制进行运算:
I P 地址 11010000.10101000.00000000.00000100
子网掩码 11111111.11111111.11111111.00000000
AND运算
11010000.10101000.00000000.00000000
转化为十进制后为:
192.168.0.0
通过以上对三组计算机IP地址与子网掩码的AND运算后,我们可以看到它运算结果是一样的。均为192.168.0.0
所以计算机就会把这三台计算机视为是同一子网。
小明灵机一动,打算做一个帮助大家解决局域网游戏问题的工具,设想如下:
我知道我的电脑的IP和子网掩码,又知道另外几个同学的ip地址,通过这个工具就可以知道我可以和谁一起CS。
IP寻址规则:
A.网络标识不能数值127开头(在A类地址中127.0.0.1是loopback IP)
B.网络标识第一个字节不能是255和0
C.IP每个字段不能大于255
子网掩码:
A. 不能全部是255;
B. 不能全部是0;
C. 掩码的高位(bit)必须是连续的1; 例如 : 255.255.252.0 --> 11111111.11111111.11111100.00000000 按照Bit来看1和0的位必须是连续的,而掩码高位都是1,所以有时候会见到 错误 : 255.255.253.0 --> 11111111.11111111.11111101.00000000 这里就出现bit为1但没有连续的情况,这种掩码是不正确的
运行时间限制: 无限制
内存限制: 无限制
输入:
第一行是我的电脑的IP地址
第二行是我的电脑的子网掩码
第三行整数N,表示后面N个同学的IP地址
第1个同学的IP地址
......
第N个同学的IP地址
输出:
计算并输出N个IP地址是否与我的电脑在同一子网内。
对于在同一子网的输出:let's rock
对于在不同子网的输出:not you
对于无效的联网IP输出:Invalid IP address.
对于无效的子网掩码:Invalid netmask address.
样例输入:
192.168.0.1
255.255.255.0
3
192.168.0.2
192.168.0.200
192.168.2.2
样例输出:
let's rock
let's rock
not you
答案提示:
参考测试用例:
测试用例1:
输入:
10.123.12.7
255.0.0.0
3
10.121.234.1
192.168.19.9
10.124.123.2
输出:
let's rock
not you
let's rock
测试用例2:
输入:
192.168.29.1
255.255.255.0
2
127.0.0.1
192.168.29.100
输出:
Invalid IP address.
let's rock
测试用例3:
输入:
10.144.240.73
255.252.253.0
输出:
Invalid netmask address.
测试用例4:
输入:
10.144.240.73
0.0.0.0
输出:
Invalid netmask address.
测试用例5:
输入:
10.146.240.256
输出:
Invalid IP address.
测试用例6:
输入:
255.148.240.1
输出:
Invalid IP address.
测试用例7:
输入:
0.147.240.15
输出:
Invalid IP address.
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class IPTest {
static String ip0 = "";
static String mask = "";
public static void main(String[] args) {
List<String> list = readDataFromConsole();
if(list==null){
return;
}
String output="";
String org = getNetSeg(ip0, mask);
for (int i = 0; i < list.size(); i++) {
String ip = list.get(i);
if(!isValidIP(ip)){
output+="Invalid IP address.\n";
}else{
if(getNetSeg(ip, mask).equals(org)){
output+="let's rock\n";
}else{
output+="not you\n";
}
}
}
System.out.println(output);
}
private static boolean isValidIP(String ip){
int[] intArr = string2intArr(ip);
if(intArr==null||intArr[0]==127||intArr[0]==255||intArr[0]==0){
return false;
}
for (int i = 0; i < intArr.length; i++) {
if(intArr[i]>255){
return false;
}
}
return true;
}
private static boolean isValidMask(String mask){
if(string2intArr(mask)==null){
return false;
}
if(mask.equals("255.255.255.255")||mask.equals("0.0.0.0")){
return false;
}
if (dec2Binary(mask).split("0").length>1){//如果合法,如111000...,split后数组长度为1
return false;
}
return true;
}
private static String dec2Binary(String ip){
int[] intArr = string2intArr(ip);
String binStr = "";
for (int i = 0; i < intArr.length; i++) {
String tmp = Integer.toBinaryString(intArr[i]);
tmp = "0000000"+tmp;
binStr += tmp.substring(tmp.length()-8, tmp.length());//为了使转换后的二进制都为8位
}
return binStr;
}
private static int[] string2intArr(String ip){
String[] strArr = ip.split("\\.");
if (strArr.length!=4){
return null;
}
int[] intArr = new int[4];
for (int i = 0; i < strArr.length; i++) {
intArr[i] = Integer.parseInt(strArr[i]);
}
return intArr;
}
private static String getNetSeg(String ip, String mask){
int[] ipArr = string2intArr(ip);
int[] maskArr = string2intArr(mask);
String res = "";
for (int i = 0; i < maskArr.length; i++) {
res+= ipArr[i]& maskArr[i];
}
return res;
}
private static List<String> readDataFromConsole() {
Scanner scanner = new Scanner(System.in);
List<String> ipList = new ArrayList<String>();
ip0 = scanner.next();
if(!isValidIP(ip0)){
System.out.println("Invalid IP address.");
return null;
}
mask = scanner.next();
if(!isValidMask(mask)){
System.out.println("Invalid netmask adress.");
return null;
}
int count = scanner.nextInt();
while (count-->0) {
ipList.add(scanner.next()) ;
}
return ipList;
}
}
分析:这题没什么难点,不过功能点比较多,所以有点耗时间。部分功能有多种实现方法,比如判断子网掩码是否不合法,可以用一个正则表达式“^1.?01.+0$”(以1开头,0结尾,中间有01的)。十进制转二进制也可以自己用除法、取余实现。
2. 自动售货系统
描述:考生需要模拟实现一个简单的自动售货系统,实现投币、购买商品、退币、输出库存商品及存钱盒信息的功能.
1. 系统中存在以下商品与钱币:
三种商品:每种商品包含商品名称、单价、数量三种属性,分别为:名称为A1的商品单价为2元数量为5件;名称为A2的商品单价为3元数量为10件;名称为A3的商品单价为4元数量为14件
存钱盒信息存在以下三种面额1、2、5元的钱币,每种钱币包括面额、张数属性,分别为:1元的 5张;2元的 5张, 5元的 3张;
2. 系统退币原则:根据系统存钱盒内钱币的信息,按钱币总张数最少的原则进行退币,即使用户只投币不购买商品也按此原则退币。
3. 约束:考生不需要考虑命令的非法性,即不存在非法的命令的情况,例如系统中A1商品只有5件,不存在买第6件的情况,不存在买的商品总价超过投币总和的等情况。
4. 输入说明
系统接收到输入命令需要初始化系统,初始化系统中商品与钱币数量。命令字与参数间使用一个空格分隔,不同命令之间用逗号分隔。
1). 投币命令:命令格式:p 钱币面额,投币可以投入多次,只能投入1、2、5元面额的钱币。
2). 购买商品:命令格式:b 商品名称
一条购买命令仅能购买一件商品,可以多次购买;购买商品成功后,自动售货机中对应商品数量减1,存钱盒中金额相应的增加,全部购买完成需要按退币原则把多余的钱币退回。
例如:p 5,p 5,p 5,b A3,b A3,b A2
5. 输出说明
输出自动售货机中商品和存钱盒的信息,包含商品名称、数量,存钱盒面额、数量。根据商品名称的从小到大顺序进行排序,输出结果中商品名称与数量使用一个空格分开,
存钱盒根据钱币面额从小到大排序输出,钱币面额与数量使用一个空格分开,不同信息间使用逗号分开,商品信息与存钱盒信息使用分号分开。
例如,返回结果为:A1 5,A2 9,A3 12;1 5,2 3,5 6
运行时间限制: 无限制
内存限制: 无限制
输入:
投币命令、购买商品命令
输出:
自动售货机中商品和存钱盒的信息
样例输入:
p 5,p 5,p 5,b A3,b A3,b A2
样例输出:
A1 5,A2 9,A3 12;1 5,2 3,5 6
答案提示:
无
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class VendingMachine {
public static void main(String[] args) {
int[] products=new int[]{5,10,14};
int[] money=new int[]{5,5,3};
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
String line = scanner.nextLine();
Pattern p = Pattern.compile("(?<=p\\s)\\d");
Matcher m = p.matcher(line);
int pay = 0;
while(m.find()){
pay += Integer.parseInt(m.group());
switch (m.group()){
case "1":
money[0]++;
break;
case "2":
money[1]++;
break;
case "5":
money[2]++;
break;
default:
break;
}
}
Pattern p2 = Pattern.compile("(?<=b\\s)\\w+");
Matcher m2 = p2.matcher(line);
int cost=0;
while(m2.find()){
switch (m2.group()){
case "A1":
products[0]--;
cost+=2;
break;
case "A2":
products[1]--;
cost+=3;
break;
case "A3":
products[2]--;
cost+=4;
break;
default:
break;
}
}
int back = pay-cost;
int back5 = back/5>money[2]?money[2]:back/5;
back = back-5*back5;
money[2]-=back5;
int back2 = back/2>money[1]?money[1]:back/2;
back = back-2*back2;
money[1]-=back2;
int back1 = back;
money[0]-=back1;
System.out.println("A1 " + products[0]+",A2 "+products[1]+",A3 "+ products[2]+",1 "+money[0]+",2 "+money[1]+",5 "+money[2]);
}
}
}
分析:这题咋一看像是要用贪婪算法或动态规划来实现,但是人家的题目条件简单,而且限定比较死,所有咱就写了最简单的代码,整个过程就是字符串匹配和加减运算。把钱币额度从大到小排(5, 2, 1),找回的钱和它们挨个作除法、取余,得到的就是最少钱币数目,当然还要考虑每种钱币的原始总数。如果给我们的钱币额度是(5,4,1)这样做就不行了。例如要找回8元,用程序中的方法得到的是(5元*1+1元*3),但最佳方案是(4元*2)。