CSP-J 2021 T3 网络连接(洛谷P7911

CSP-J 2021 T3 网络连接

题目

一个大模拟,考场上唯一一道算是知道怎么做但最后还是被坑了的。坑点不算太多但比较细,不过有两个神器可以完美解决大部分的细节,它就是——sscanfsprintf

用法

在这里稍微介绍一下用法:sscanfscanf基本一样,只不过scanf是从终端获取输入,而sscanf是从一个字符串获取输入。sprintfprintf基本一样,只不过printf是输出到终端,而sprintf是输出到一个字符串内。用法跟scanfsprintf的差别只是要在最开始加入你要输入/输出的字符串并在后面加,

这两种方法可以帮助我们很方便的完成这道题。

实例:

自己看百度百科吧,不会讲了qwq(光速逃跑)

百度百科sscanf 百度百科sprintf

思路

对地址格式正确性的判断

先将地址存到一个字符串内,然后我们通过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都会被省略,无论是cinscanf还是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;
}
posted @ 2022-03-07 18:58  AC?别闹!  阅读(384)  评论(0编辑  收藏  举报