CCF 201812-3 CIDR合并

样例输入

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;
}

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/87727916