试题名称 | 代码长度 | 编程语言 | 评测结果 | 得分 | 时间使用 | 空间使用 |
---|---|---|---|---|---|---|
CIDR合并 | 2.576KB | C++ | 正确 | 100 | 171ms | 3.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;
}