[CCF/CSP]201812-(3) CIDR合并 C++ 100分

试题名称代码长度编程语言评测结果得分时间使用空间使用
CIDR合并2.576KBC++正确100171ms3.906MB

关键点:

1. 字符串处理    用sscanf( ) 简洁方便

2. IP点分十进制转为32位无符号整数   

3. 前缀转为子网掩码。判断能否合并时,ip和子网掩码作“与”运算更快

4. 用list存储和处理列表。  有大量删除操作,所以用链表,用顺序表的话超时,最多得90分。

#include <stdio.h>
#include <string.h>
#include <string>
#include <list>

using namespace std;

struct IP{
    int prefix;//前缀长度
    unsigned int ip;//ip的32位无符号整数形式
    IP():prefix(-1){}//前缀默认为-1,以此来判断输入的一行中给没给“/前缀”
};

bool sortList(IP a, IP b){// list 的排序规则
    if(a.ip != b.ip){
        return a.ip < b.ip;
    }else if(a.prefix != b.prefix){
        return (a.prefix) < (b.prefix);
    }else{
        return false;
    }
}


unsigned int getMask(int prefix){//根据前缀长度计算子网掩码
    if(prefix==0)
        return 0;
    if(prefix==32)
        return 0xffffffff;
    unsigned int mask=0;
    for(int i =0; i<prefix; i++){//前缀是几,mask的二进制高位就有几个1
        mask = (mask<<1)+1;
    }
    return mask<<(32-prefix);//把这几个1推到高位
}

void setIP(char line[20], list<IP> &ipList){//根据输入的一行字符串,获取ip和前缀

    //dec[4]分别存了ip点分十进制形式的四个十进制数
    //必须初始化为0,否则没读到值的都成了随机数
    unsigned int dec[4]={0};    
    
    char ipstr[20];
    IP tmp;

    //%[^/]表示读取到/停止,如果没有/,则&tmp.prefix读不到值,会保持默认的-1不变
    sscanf(line, "%[^/]/%d", ipstr, &tmp.prefix);

    //sscanf返回了成功读到几个数 
    int cnt = sscanf(ipstr, "%u.%u.%u.%u", &dec[0], &dec[1], &dec[2], &dec[3]);    

    if(tmp.prefix==-1) tmp.prefix=cnt*8;

    //点分十进制转为32位无符号整数
    tmp.ip = (dec[0]<<24) ^ (dec[1]<<16) ^ (dec[2]<<8) ^ dec[3];    

    ipList.push_back(tmp);

}



void printIP(IP ip){//打印一行IP,ip的32位无符号整数转为点分十进制
    unsigned int dec0=0xff000000, dec1=0x00ff0000, dec2=0x0000ff00, dec3=0x000000ff;
    printf("%u.%u.%u.%u/%d", (ip.ip&dec0)>>24, (ip.ip&dec1)>>16, (ip.ip&dec2)>>8, (ip.ip&dec3), ip.prefix);
}

void printipList(list<IP> &ipList){
    for(list<IP>::iterator it = ipList.begin(); it!=ipList.end(); it++){
        printIP(*it);
        printf("\n");
    }
}


void merge1(list<IP> &ipList){//合并能被别的网段包含的ip
    list<IP>::iterator itL;
    list<IP>::iterator itR = ipList.begin();
    itR++;
    for(itL = ipList.begin(); itL!=ipList.end();){

        unsigned int mask = getMask((*itL).prefix);

        if( ((*itL).ip & mask) == ((*itR).ip & mask) ){
            ipList.erase(itR++);//删掉并将itR指向下一个元素
        }else{
            itL++;
            itR++;
        }
    }
}

void merge2(list<IP> &ipList){//将恰好能合并为 前缀-1的网段 的两个网段合并
    list<IP>::iterator itL;
    list<IP>::iterator itR = ipList.begin();
    itR++;
     for(itL = ipList.begin(); itL!=ipList.end();){
        unsigned int mask = getMask((*itL).prefix-1);
        if((*itL).prefix == (*itR).prefix && ((*itL).ip&mask) == ((*itR).ip&mask)){
                (*itL).prefix--;
                ipList.erase(itR++);
                if(itL != ipList.begin()){
                   itL--;
                   itR--;
                }
        }else{
            itL++;
            itR++;
        }

    }
}

int main(){
//    freopen("201812-3-CIDR.txt","r", stdin);

    int n, i;
    char line[20];
    scanf("%d", &n);
    getchar();//吸收掉n后面的回车
    list<IP> ipList;
    for(i=0; i<n; i++){
        gets(line);
        setIP(line, ipList);
    }

    ipList.sort(sortList);

    merge1(ipList);
    merge2(ipList);
    printipList(ipList);
    return 0;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值