201812-3 CCF-CSP CIDR合并

备注:CCF CSP第三题 题目都很长,会引入其他你没学过的知识,不过给与的解释肯定能看懂,所以这里大家一定需要坚持住, 把题目全部看完,相信自己,我不勇敢谁替我坚强!

样例输入

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

 

By the way: 这是 CCF CSP 的第三题 ,也是题目特别长,和实际结合特别密切的题目,笔者去年做这一题,虽然看懂了题目,还是感慨自己的编程水平不够,没有得分。这一次再认真阅读完其他人的博客信息,了解了如何解题,参照他人的方法,自己模仿着写出来了,借鉴了别人的代码和学习了他人一些编程的相关函数,学习了 list 双向链表的使用,也算是收货颇多吧。

解题思路:

1、关键就是 解决IP地址、IP前缀问题,我们使用一个结构体来储存,ip和len。题目已经告知输入的IP前缀 一定正确,不过IP地址存在三种情况:标准型、省略长度型、省略后缀型,还是就是为了合并或者排序,我们需要把IP变为2进制,也就是变为32位数,这样就可以使用一个普通的字符串符号比较就可以来判断大小了。

2、为了排序和合并方便,我们采用链表来储存,因为链表便于插入和删除,尤其是合并的删除,十分方便,采用STL中的list容器来储存,我们使用sort函数,重写cmp,将所有结构体储存之后,排序。

3、从小到大合并,我们使用了 Merge1()函数,一个j来遍历,一个i,先判断是否是匹配集,根据len和ip值来判断,这里特别说一下len(掩码), 它要求长度为len的数字必须相同。使用我们判断是否可以匹配,如果可以匹配则删除范围小的,然后j指向下一个,如果不能匹配,则i和j同时指向下一个。

4、同级合并,我们使用了 Merge2()函数,一个j来遍历,一个i,先判断是否为同级,如果为同级,则需要将j删除,然后i需要将len长度减1,同时看见 i之前是否还有元素,如果之前还有元素,需要回退一步,否则继续从i开始考虑(题目提供的解题思路已经给出)。

5、输出 需要 把二进制转换为 十进制进行输出,同时需要注意 输出格式 为标准型。

#include <iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<list>
#include<cctype>
using namespace std;
struct IP {
	string ip="";
	int len = -1;

};
IP toip(string s) {  //得到一个长度为32位的二进制编码字符串
	IP ipadr;
	string ipstr;
	for (int i = 0; i <= s.size(); i++) {   //等于 s.size()是为了 处理为数字仅有 1位的情况
		if (i == s.size() || !isdigit(s[i])) {
			int k = stoi(ipstr);//转化为 数字
			int d = 128;
			while (d > 0) {   //转换为二进制数
				if (k / d) {
					ipadr.ip+= "1";
					k -= d;
				}
				else {
					ipadr.ip += "0";
				}
				d /= 2;
			}
			if (s[i] == '/') {    //结束循环
				ipadr.len = stoi(s.substr(i + 1));//得到ip前缀
				break;     
			}
			ipstr = "";    //归为空
		}
		else ipstr += s[i];   //数字
	}
	if (ipadr.len == -1) {   //省略长度型
		ipadr.len = ipadr.ip.size();
	}
	while(ipadr.ip.size() < 32) {   //省略后缀型
		ipadr.ip += "0";
	}
	return ipadr;

}

bool cmp(const IP &ip1,const IP & ip2) {
	if (ip1.ip == ip2.ip) return ip1.len < ip2.len;
	else return ip1.ip < ip2.ip;
}

bool isChild(IP &a, IP &b) {//判断b是否是a匹配集的子集
	if (a.len > b.len) {    //a的掩码比b长
		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(); //让l去下
		++j;// j访问下一个
		while (j != l.end()) {
			if (isChild(*i, *j)) { //如果 j是 i的子集 则把j删除
				j = l.erase(j);   //返回被删除元素的下一个元素位置
			}
			else {
				i++;
				j++;
			}
	}

}
bool Issamelevel(IP &a, IP &b) {//判断a是否和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();
	j++;
	while (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> ipadr;
	while (n--) {
		string s;
		cin >> s;
		ipadr.push_back(toip(s));
	}
	ipadr.sort(cmp);
	

	merge1(ipadr);
	merge2(ipadr);   //得出来 ip 为 32位

	for (auto it = ipadr.begin(); it != ipadr.end();it++) {
		for (int i = 0; i < 4; i++) {  //三部分
			int k = 0;
			for (int j = 0; j < 8; j++)  //8位二进制数转为十进制
			{
				k = k * 2 + ((*it).ip[j + 8 * i] - '0');
			}
			cout << k;
			i < 3 ? cout << "." : cout << "/";
		}
		cout <<(*it).len << endl;
	}

	return 0;
}

 

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值