CSP-J 2021 T3 网络连接(洛谷P7911
CSP-J 2021 T3 网络连接
一个大模拟,考场上唯一一道算是知道怎么做但最后还是被坑了的。坑点不算太多但比较细,不过有两个神器可以完美解决大部分的细节,它就是——sscanf
和sprintf
!
用法
在这里稍微介绍一下用法:sscanf
跟scanf
基本一样,只不过scanf
是从终端获取输入,而sscanf
是从一个字符串获取输入。sprintf
跟printf
基本一样,只不过printf
是输出到终端,而sprintf
是输出到一个字符串内。用法跟scanf
和sprintf
的差别只是要在最开始加入你要输入/输出的字符串并在后面加,
这两种方法可以帮助我们很方便的完成这道题。
实例:
自己看百度百科吧,不会讲了qwq(光速逃跑)
思路
对地址格式正确性的判断
先将地址存到一个字符串内,然后我们通过sscanf
提取出地址内的数字,并用sscanf
中的格式控制符来判断地址的格式是否正确,因为sscanf
按照给的格式控制符从字符串内提取信息,所以如果格式不对的话是有些数字提取不到的。
具体做法:
char address[30];
//sscanf不能用string类型,只能用char数组
cin>>address;
int a,b,c,d,e;
if(sscanf(address,"%d.%d.%d.%d:%d",&a,&b,&c,&d,&e)!=5)
{ //通过地址标准的格式来判断给的地址格式是否标准,!=5是为了判断数字个数是否足够
//sscanf的要输入的元素跟scanf一样之前要加取地址符
cout<<"ERR"<<endl;
}
通过sscanf
这个神奇的小东西,我们成功地判断了地址的格式是否正确,接下来就是判断数字有没有超出范围和有没有前导0了。
判断数字有没有超出范围很简单,直接暴力if
就行了,但是要记得判断<0的情况,因为题目没说数字都是非负整数而且格式内要求数字\(\ge0\)。
判断有没有前导0时可以用暴力,遍历整个字符串进行判断;但是我之前介绍过的sprintf
可不只是介绍一下的。我们可以通过sprintf
将数字按照正确的格式存在一个字符串内,然后将两个字符串(一个输入的一个我们新建的)进行比较,如果不同,那就是有前导0,因为在输入数字的时候前导0都会被省略,无论是cin
、scanf
还是sscanf
的机制都是这样,而且我们之前通过sscanf
已经对格式的正确进行了判断所以不会出现格式不同的情况。这里我加了个优化,提前判断长度是否改变,如果改变了那么就不用进行循环判断从而节省时间。
具体做法:
char s[30];
//按照格式输入到字符串内
sprintf(s,"%d.%d.%d.%d:%d",a,b,c,d,e);
if(strlen(s)!=strlen(ad)){
//提前判断s的长度是否改变,省去后面循环
cout<<"ERR"<<endl;
continue;
}
bool ok=1;
//char数组不能用==,只能循环判断
for(int j=0;j<strlen(ad);++j){
if(ad[j]!=s[j]){
ok=0;
break;
}
}
if(!ok){
cout<<"ERR"<<endl;
continue;
}
这样的话对于地址的判断就完成了,我们就可以开始进行最简单的一步了——进行连接
进行连接
根据计算机的类型进行连接,我们用一个map<string,int>
来存储服务器的地址和编号。如果是服务器,我们就判断有没有相同的地址,有的话就输出FAIL
,没有就连接并将地址和编号存进map
里并输出OK
。如果是客户机的话就判断这个地址有没有被服务器连接,有的话就直接输出map
中存储的编号,没有直接输出FAIL
即可。
完整代码
点击查看代码
#include<iostream>
#include<cstdio>
#include<sstream>
#include<cstring>
#include<map>
using namespace std;
//存放服务器地址和编号
map<string,int> m;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;++i){
char op[30],ad[30];
cin>>op>>ad;
int a,b,c,d,e;
//从字符串内读入,判断格式和数字个数是否正确
if(sscanf(ad,"%d.%d.%d.%d:%d",&a,&b,&c,&d,&e)!=5){
cout<<"ERR"<<endl;
continue;
}
//暴力判断数字是否超出范围
if(a<0||a>255||b<0||b>255||c<0||c>255||d<0||d>255||e<0||e>65535){
cout<<"ERR"<<endl;
continue;
}
//判断是否有前导0
//把所有的数字按照正确格式存进一个字符串,
//因为在输入数字时前导0会被省略,
//所以若两个字符串相等则没有前导0
char s[30];
//sprintf跟sscanf用法基本相同,
//sscanf是从字符串内输入,sprintf是输出到字符串内
sprintf(s,"%d.%d.%d.%d:%d",a,b,c,d,e);
if(strlen(s)!=strlen(ad)){
//提前判断s的长度是否改变,省去后面循环
cout<<"ERR"<<endl;
continue;
}
bool ok=1;
for(int j=0;j<strlen(ad);++j){
if(ad[j]!=s[j]){
ok=0;
break;
}
}
if(!ok){
cout<<"ERR"<<endl;
continue;
}
//判断地址格式完毕,将地址保存
if(op[0]=='S'){
if(m[ad]==0){
//如果没有地址相同的服务器
//存储这个服务器的编号
m[ad]=i;
cout<<"OK"<<endl;
continue;
}
else{
cout<<"FAIL"<<endl;
continue;
}
}
else{
if(m[ad]==0){
//如果没有客户机要连接的地址的服务器
cout<<"FAIL"<<endl;
continue;
}
else{
cout<<m[ad]<<endl;
continue;
}
}
}
return 0;
}