样例输入
2
1
2
样例输出
1.0.0.0/8
2.0.0.0/8
样例输入
2
10/9
10.128/9
样例输出
10.0.0.0/8
样例输入
2
0/1
128/1
样例输出
0.0.0.0/0
PS:又到了ccf最烦人的第三题大模拟,往往耗费几小时还得不到满分,代码越简洁越不容易扣分。
分析:
本题较以往的大模拟更为复杂,要求我们模拟CIDR聚合构成超网的过程。没学过计算机网络的理解起来比较耗时间,好在题目提供了解题思路。
本题代码有参考另一位大佬,下面详细说下设计思路。
第一步:确定存储结构。
输入若干个ip地址,要求输出合并后的标准型IP地址。输入的CIDR地址由两部分构成,ip地址和网络前缀长度。理所应当的想到用一个结构体存储地址,结构体两个变量分别是地址和长度。并且定义一个列表存储该结构体类型的那么多地址。
第二步:处理下输入的地址。
题目给的思路需要排序,所以为了统一格式,将输入的地址统一转换为标准的32位二进制地址。一段段来,遇见点或者/之前,将该段字符先转换为整数再转换为二进制表示。输入地址中有长度则直接赋值,没有则根据输入地址转换为二进制数字的长度算。最后,对缺省的地址在末尾补0.
第三步:排序并两次合并。
合并操作直接在主函数里调用list自带的sort并重载它。第一次合并是从小到大合并,后一个元素是前一个元素匹配集的子集则删除后面的元素;第二次合并是同级合并,两个地址长度相同而且地址前缀长为len-1的部分都对应相等才能合并。与之前合并操作不同的是,这里删除元素后由于地址的长度减一了,还需要与之前的地址比较,所以删除之后双指针都要回退。
虽然整体思路并不复杂,但是每个函数内部的处理细节却较为繁琐。总之,代码越简洁越好。
#include <iostream>
#include <cctype>
#include <list>
using namespace std;
struct IP{
string ip = "";//IP地址
int len = -1;//网络前缀长度
};
IP toIp(string s){//转化为32位二进制ip地址
IP ipadress;
string ipa;
for(int i = 0;i <= s.size();i++){
if(i == s.size() || !isdigit(s[i])){//遇到非数字或者到结尾
int k = stoi(ipa);//c++11里面字符串转化为整型的函数
int d = 128;
while(d > 0){//进制转换
if(k / d){
ipadress.ip += "1";
k -= d;
}
else ipadress.ip += "0";
d /= 2;
}
if(s[i] == '/'){
ipadress.len = stoi(s.substr(i+1));
break;
}
ipa = "";
}
else ipa += s[i];
}
if(ipadress.len == -1) ipadress.len = ipadress.ip.size();//省略长度型
while(ipadress.ip.size() < 32){//省略后缀型
ipadress.ip += "0";
}
return ipadress;
}
bool isChild(IP &a,IP &b){//判断b是否是a匹配集的子集
if(a.len > b.len) return false;
for(int i = 0;i < a.len;i++){
if(a.ip[i] != b.ip[i]) return false;
}
return true;
}
void merge1(list<IP> &l){//从小到大合并
auto i = l.begin(),j = l.begin();
for(++j;j != l.end();){
if(isChild(*i,*j)){
j = l.erase(j);//返回被删除元素的下一个元素位置
}
else{
i++;
j++;
}
}
}
bool isSameLevel(IP &a,IP &b){//是否同级
if(a.len != b.len) return false;
for(int i = 0;i < a.len - 1;i++){
if(a.ip[i] != b.ip[i]) return false;
}
return true;
}
void merge2(list<IP> &l){//同级合并
auto i = l.begin(),j = l.begin();
for(++j;j != l.end();){
if(isSameLevel(*i,*j)){
j = l.erase(j);
(*i).len--;
if(i != l.begin()){
i--;
j--;
}
}
else{
i++;
j++;
}
}
}
int main(){
int n;
cin>>n;
list<IP> adr;
while(n--){
string s;
cin>>s;
adr.push_back(toIp(s));
}
adr.sort([](const IP&a,const IP&b){
if(a.ip == b.ip) return a.len < b.len;
return a.ip < b.ip;
});
merge1(adr);
merge2(adr);
for(auto&it:adr){
for(int i = 0;i < 4;i++){
int k = 0;
for(int j = 0;j < 8;j++)
k = k * 2 + (it.ip[j+8*i]-'0');
cout<<k;
i < 3 ? cout<<"." : cout<<"/";
}
cout<<it.len<<endl;
}
return 0;
}