CCF-CSP认证 202104-3 DHCP服务器

CCF-CSP认证 202104-3 DHCP服务器

题意描述

这次要求我们实现一个简易的DHCP服务器 我们需要处理DIS REQ 报文段 发送 NAK ACK OFR 报文段 对于每个接收到的报文段 我们需要判断该报文是否有效 若是有效的DIS 报文 我们要回复OFR 报文 报文中含有服务器编号 请求主机编号 报文类型 申请得到的IP地址 IP地址的过期时刻 若是有效的REQ报文 我们要根据目的主机的不同 以及IP地址的有效性作出不同的回复

输入输出格式

输入第一行 是服务器的配置 包含 地址池大小 普通时长 最短时长 最长时长 服务器的名字
第二行 是总共有多少个DHCP请求报文段
接下来 便是各个报文 格式为 服务器收到报文的时间 发送主机 目的主机 报文类型 IP地址 希望过期的时刻

我们要输出DHCP服务器的回复

数据规模

地址池最大可以达到1e4

算法设计

  1. 模拟题最重要的就是存储结构的设计 这里 我们设计一个结构体 IP 有成员 onwer ed 我们将1 到 N 的IP地址存储在数组中 下标直接映射为IP地址 这样 从前到后遍历数组 便实现了IP地址从小到大的枚举要求 onwer是该IP地址的占有者 ed 是该IP地址的过期时刻 再设计一个flag数组 下标同样映射数组 值表示着该IP地址的状态 0 表示未分配 1 表示待分配 2 表示占有 3 表示过期
  2. 将各个遍历直接定义为全局变量 此后函数就不用传参数了
  3. 题中对服务器要处理的报文类型描述较多 但总结下来 就是 我们只需要处理 类型为DIS 且 目的主机为*的 或是 类型为 REQ 且 目的主机不为*的 其余直接忽略
  4. 接下来对报文类型进行分类 若为DIS 先求分配的IP地址 循环遍历IP数组 先找占有者是源主机的 若没有 再找状态为未分配的 最后找过期的 若分配不到IP地址 直接跳过 若分配了IP地址 计算过期时间 即可发送报文
  5. 若为REQ 先判断目的主机是不是我们 若不是 将数组中所有占有者为源主机的 而且 处于待分配地IP地址地状态均转换为未分配 占有者清空 若目的主机是我们 我们需要在IP数组中寻找该IP地址的占有者是不是源主机 若不是 发送NAK报文 若是 发送 ACK 报文
  6. 整体的框架就是这样了 但是我们必须注意最重要的细节 提交得到70的 应该就是这些细节没有处理到位
  7. 细节一 每次我们处理报文前 必须根据现在的接收报文的时刻 和 每个IP地址的过期时刻 将每个IP地址的状态刷新 主要有两个状态转移 第一个是 处于待分配的IP地址在过期之后 转换为未分配的IP地址 占有者清空 第二个是 处于占有状态的IP地址在过期之后转换为过期状态 占有者不变 我们更新状态的原因很简单 处理报文时要分配IP地址 我们必须准确获得此刻每个IP地址的状态 才能作出相应的处理
  8. 细节二 在处理REQ报文时 当目的主机不是我们 我们要枚举所有的IP地址 更新状态
  9. 我在第一次实现时 顺利得到超时的结果 😂 原因是 我没有写结构体 而是把IP数据用map<gg,pair<string,gg>>存储起来 还有map<gg,gg> 表示状态 由于map的键是升序的 我遍历map感觉也能满足要求 但在程序中 我们要频繁地对每个IP地址的状态进行更改 就意味中 我们要频繁地访问状态map map底层是红黑树 由键查找的复杂度达到了 O(logN) 果断超时 之后改用数组下标映射IP地址 数组值直接表示IP地址的状态 就过了 所以 以后还是尽量用数组下标做映射 数组的下标就相当于map的键 不过 这个题感觉数据没有说的那么大 平方级算法也是过的去的
    希望读者在这些引导之下 能够独立编码 调试 解决问题

c++11代码

#include <bits/stdc++.h>
using namespace std;
using gg = long long;
struct IP{
    string owner;
    gg ed;
};
vector<IP>mp(1);   //  初始长度为1
vector<gg>flag(1);   //  下标是 ip地址
gg N, t, tmax, tmin, ni, nowtime, endtime, ip;
string hname, from, to, type; //   hname 服务器名字
bool judge(){
    if(type =="DIS" and to =="*") return true;
    if(type == "REQ" and to != "*") return true;
    return false;
}
void brush(){   // 更新状态
    for(gg i=1;i<=N;i++){
        if(mp[i].ed <= nowtime){
            mp[i].ed =0;    //   过期时间清零
            if(flag[i] ==1) {
                flag[i] = 0;   //  待分配的超时 转换为未分配
                mp[i].owner ="";
            }else if(flag[i] == 2){
                flag[i] =3;  //  占用的超时 转换为 过期
            }
        }
    }
}
gg findip(){
    for(gg i =1;i<=N;i++) if(mp[i].owner == from) return i;
    for(gg i =1;i<=N;i++) if(flag[i] == 0) return i;
    for(gg i =1;i<=N;i++) if(flag[i] == 3) return i;
    return 0;
}
gg findtime(){
    if(endtime == 0) return nowtime + t;
    if(endtime < nowtime + tmin) return nowtime + tmin;
    if(endtime > nowtime + tmax) return nowtime + tmax;
    return endtime;
}
int main()
{
   ios::sync_with_stdio(false);
   cin.tie(0);
   cout.tie(0);
   cin >> N >> t >> tmax >> tmin >> hname;
   cin >> ni;
   for (gg i = 1; i <= N;i++){
       mp.push_back({"",0});
       flag.push_back(0);  //  均未分配
   }
    while (ni--){
       cin >> nowtime >> from >> to >> type >> ip >> endtime;
       if(not judge()) continue;
       brush();
       if(type == "DIS"){
           gg rip = findip();
           if(not rip) continue;
           gg time = findtime();
           flag[rip] = 1;   // 标志为 待分配
           mp[rip].owner = from;
           mp[rip].ed = time;
           cout<<hname<<" "<<from <<" "<<"OFR"<<" "<<rip<<" "<<time<<"\n";
       }else{
           if(to != hname) {   //  我不是目标主机
                for(gg i =1;i<=N;i++) {
                    if(mp[i].owner == from and (flag[i] == 1)) {
                        flag[i] =0;
                        mp[i].ed =0;  //  时间置0
                        mp[i].owner = "";  //  所有者为空
                    }
                }
           }else{
               if(ip >N or mp[ip].owner != from){   //   短路 确保不会越界
                   cout<<hname<<" "<<from<<" "<<"NAK"<<" "<<ip<<" "<<0<<"\n";
               }else{
                   gg time = findtime();
                   mp[ip].ed = time;
                   flag[ip] = 2;   //  占用
                   cout<<hname<<" "<<from<<" "<<"ACK"<<" "<<ip<<" "<<time<<"\n";
               }
           }
       }
    }
   return 0;
}

题目链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值