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

试题名称 代码长度 编程语言 评测结果 得分 时间使用 空间使用
CIDR合并 2.576KB C++ 正确 100 171ms 3.906MB

关键点:

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

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

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

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

#include 
#include 
#include 
#include 

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 &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 &ipList){
    for(list::iterator it = ipList.begin(); it!=ipList.end(); it++){
        printIP(*it);
        printf("\n");
    }
}


void merge1(list &ipList){//合并能被别的网段包含的ip
    list::iterator itL;
    list::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 &ipList){//将恰好能合并为 前缀-1的网段 的两个网段合并
    list::iterator itL;
    list::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 ipList;
    for(i=0; i

 

你可能感兴趣的:(学习笔记,我的代码)