CSP 202104-3 大模拟

3 DHCP服务器

因为是个大模拟,题目又臭又长还有点儿绕,解答本题要好好理解题意,按照题目要求来写。

题意

CSP 202104-3 大模拟
DHCP 数据报文的格式:

<发送主机> <接收主机> <报文类型> <IP 地址> <过期时刻>
 string    string     DIS
              *       OFR
                      REQ
                      ACK
                      NAK

CSP 202104-3 大模拟
CSP 202104-3 大模拟

Round 1

代码按照题目给出的细节要求一步步进行即可,但是需要注意一些细节处理,例如条件之间的关系该用与还是或。
CSP 202104-3 大模拟
问题主要出现在这个地方:out给出的是过期的时刻,所以这里要判断 过期时间是否超过上下限
CSP 202104-3 大模拟
注意到:样例解释中的 第 13、14 个报文中,e 试图请求地址。此时地址池中已经没有处于“未分配”状态的地址了,但是有此前分配给 b 的地址 2 的状态是“过期”,因此把该地址重新分配给 e。 就可以发现 你忘记处理过期的地址池了。

根据:
CSP 202104-3 大模拟
在处理每条命令前,要处理一下过期的IP:
CSP 202104-3 大模拟
等会儿!!! 这里 过期时刻应该是 ip[i].out<=t 而不是刚刚好=!!!你要没发现的话 又该要debug很久了???你写啥呢???大模拟最重要的就是细节啊!!! 仔细点儿!!!

Round 2

进行了以上修改后,基本上可以得到比较好的结果了,如下结果基本正确,但最后一条命令没能读入处理???
CSP 202104-3 大模拟
此时得分只有40分,那么 为什么会提前终止了呢???

啊 笨蛋 让你乱用 break ,
CSP 202104-3 大模拟
因为这里有个 break ,刚好你第15个命令就走到了这里…然后break出读命令的循环了…
![在这里插入图片描述](https://www.icode9.com/i/ll/?i=d1137b5dcebc43adb1cb50e0589e2978.png = 500x)
噗 改完这个bug还是只有60分…样例能过了,但肯定还存在其他没有考虑到的细节!!!

Round 3

噗噗…吐得一脸好血…
CSP 202104-3 大模拟
这里这里…CSP 202104-3 大模拟
debug de那么久…没发现是应该 ip[j].own==send 吗…之前写的是 ==recieve 啊老天鹅…这要不是对了一遍算法流程还真发现不了?

我还是太粗心了…

完整代码

//#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=1e4+5;
struct node{
    int state;// 0未分配 1待分配 2占用 3过期
    int out;//过期时刻
    string own;//占用者
}ip[maxn];
int main()
{
    int N,tdef,tmax,tmin,n;
    string H;
    cin>>N>>tdef>>tmax>>tmin>>H;
    for(int i=1;i<=N;i++)
    {
        ip[i].state=0;
        ip[i].out=0;
        ip[i].own="";
    } 
    cin>>n;
    for(int k=0;k<n;k++) 
    {   
        string send,receive,type;
        int address,t,out;
        //<收到报文时刻> <发送主机> <接收主机> <报文类型> <IP 地址> <过期时刻>
        cin>>t>>send>>receive>>type>>address>>out;
        //cout<<"y  "<<t<<endl;
        //cout<<"i "<<t<<" "<<send<<" "<<receive<<" "<<type<<" "<<address<<" "<<out<<endl;
        //cout<<"-!-"<<endl;
        //cout<<type<<endl;
        for(int i=1;i<=N;i++)//遍历所有IP
        {
            if(ip[i].state==1&&ip[i].out<=t) //如果过期时刻比当前时刻早
            {
                ip[i].state=0;
                ip[i].own="";
                ip[i].out=0;

            }else if(ip[i].state==2&&ip[i].out<=t)
            {
                ip[i].state=3;
                ip[i].out=0;
            }else if(ip[i].state==3||ip[i].state==0)
            {
                ip[i].out=0;
            }
        }

        if((receive!=H&&receive!="*")&&type!="REQ") // 同理 这里的也应该是&& 而且之前放错位置了!这个应该在第一个进行判断
            continue;
        
        //这样也是ok的
        if(type!="REQ"&&type!="DIS") //你怎么能在这用||呢?这样满足第一个条件就算是DIS也会continue掉
            continue;
        //if(type=="REQ"||type=="DIS")
        //{ 
        if((receive=="*"&&type!="DIS")||(receive==H&&type=="DIS"))
            continue;
        
        if(type=="DIS")
        {
            int rip=-1;
            for(int j=1;j<=N;j++)
            {
                if(ip[j].state&&ip[j].own==send) // 只要不是0未分配状态,就都有占用者
                {
                    rip=j;
                    break;
                }    
            }
            if(rip==-1) // 若没有,则选取最小的状态为未分配的 IP 地址;
            {
                for(int j=1;j<=N;j++)
                {
                    if(!ip[j].state)
                    {
                        rip=j;
                        break;
                    }
                }
            }
            if(rip==-1) // 若没有,则选取最小的状态为过期的 IP 地址;
            {
                for(int j=1;j<=N;j++)
                {
                    if(ip[j].state==3)
                    {
                        rip=j;
                        break;
                    }
                }
            }
            if(rip!=-1) // 找到了可用ip,可继续处理
            {
                ip[rip].state=1;
                ip[rip].own=send;
                if(out==0)
                    ip[rip].out=t+tdef;
                else if(out-t>=tmax)
                    ip[rip].out=t+tmax;
                else if(out-t<=tmin)
                    ip[rip].out=t+tmin;
                else 
                    ip[rip].out=out;
                cout<<H<<" "<<send<<" OFR "<<rip<<" "<<ip[rip].out<<endl;
            }
            //if(rip==-1)
            //cout<<"not enough"<<endl;
        }else if(type=="REQ")
        {
            if(receive!=H) //发给非本服务器的 不予应答
            {
                for(int j=1;j<=N;j++)
                {
                    if(ip[j].own==send&&ip[j].state==1)
                    {
                        ip[j].state=0;
                        ip[j].own="";
                        ip[j].out=0;
                    }
                }
            }else //注意后半部分要包在else里 
            {
                //if(!(address>=1&&address<=N&&ip[address].own==send))
                if(address<1||address>N||ip[address].own!=send)
                {
                    cout<<H<<" "<<send<<" NAK "<<address<<" 0"<<endl;
                }else{
                    ip[address].state=2;
                    if(out==0)
                        ip[address].out=t+tdef;
                    else if(out-t>tmax) // ???你脑子瓦特啦 out-t 才是有效时间,out给出的是过期时刻
                        ip[address].out=t+tmax;
                    else if(out-t<tmin)
                        ip[address].out=t+tmin;
                    else
                        ip[address].out=out;
                    cout<<H<<" "<<send<<" ACK "<<address<<" "<<ip[address].out<<endl;
                }  
            }
        }
        //}
    }
    //cout<<"out"<<endl;
    return 0;
}

总结

  大模拟需要的就是耐心和仔细,其实并不难,但是如果不注意的话,写bug的概率会比较大,就像笔者上面干的蠢事一样…2333
  其实可以不看原理部分,而是直接从实施细节开始就好了,前面都没啥用2333,但当然 你要知道一点DHCP工作原理啦…当然不知道其实也可以,一步步按照细节说的去写流程代码就好啦。
  最后 还是 细节 细节 细节!!!

上一篇:题解 P5658 [CSP-S2019] 括号树


下一篇:最大约数和-CSP