当前位置: 代码迷 >> 综合 >> CCF201812-3,CIDR合并
  详细解决方案

CCF201812-3,CIDR合并

热度:6   发布时间:2024-01-09 15:20:29.0

嗯,折腾了一上午,终于过了,这个题说难也难,说简单也简单,如果看着分数调bug满分还是挺容易的,但是考试时候一次性通过的可能就比较低了。。。

要写出来主要是要意识到两点,一个是从小到大合并的时候,要比较的是小的那个ip的前缀长度范围内两个ip是否相等,如果前缀长度范围内相等,那么另外一个一定是子集;二是同级合并的时候,存在递归合并的可能性,比如先一个23,然后两个24,两个24合并之后会生成一个23,他们是可以合并的,因此在实现的时候需要在合并的时候把迭代器向前退一个元素

再然后这个题用的人用字符串表示,我觉得读出来数字之后构造bitset更适合一些,各种操作也都很顺手。

using namespace std;
#include<cstdio>
#include<list>
#include<bitset>
struct ip{
    int a,b,c,d;bitset<32> uip;int arr[5];int suf;ip(int ia,int ib=-1,int ic=-1,int id=-1,int isuf=-1){
    int zero_count = 0;if(isuf==-1){
    if(ib==-1)zero_count++;if(ic==-1)zero_count++;if(id==-1)zero_count++;isuf = 32-zero_count*8;}suf = isuf;a = ia;if(ib == -1)ib=0;if(ic == -1)ic=0;if(id == -1)id=0;b=ib;c=ic;d=id;unsigned long ui = 0;ui+=a;ui<<=8;ui+=b;ui<<=8;ui+=c;ui<<=8;ui+=d;uip = ui;arr[0] = ia;arr[1] = ib;arr[2] = ic;arr[3] = id;arr[4] = suf;}void show(){
    printf("%d.%d.%d.%d/%d\n",a,b,c,d,suf);}
};ip create(){
    int iparr[] = {
    0,-1,-1,-1,-1};char split;int index = 0;while(index<5){
    scanf("%d",&iparr[index]);//每次读一个数split=getchar();if(split=='.'){
    //根据数之后接的符号判断是否结束index++;continue;}else if(split == '/'){
    index=4;}else{
    //行尾了break;}}return ip(iparr[0],iparr[1],iparr[2],iparr[3],iparr[4]);
};int cmp(const ip &a,const ip &b){
    if(a.a!=b.a){
    return a.a<b.a;}else if(a.b!=b.b){
    return a.b<b.b;}else if(a.c!=b.c){
    return a.c<b.c;}else if(a.d!=b.d){
    return a.d<b.d;}else{
    return a.suf<b.suf;}
}bool issub(ip &a,ip &b){
    bitset<32> l = a.uip;bitset<32> r = b.uip;int i = 0;for(i=31;i>31-(a.suf);i--)//因为大小关系已经排列好了,所以a的前缀长度一定小于等于b{
    if(l[i] != r[i])//如果前缀范围内有一个不相等,那就不是子集关系{
    return false;}}return true;
}bool merge(ip &a,ip &b)
{
    if(a.suf != b.suf || a.uip[32-a.suf] == 1 || a.suf == 0){
    return false;}bitset<32> l = a.uip;bitset<32> r = b.uip;int i = 0;for(i=0;i<a.suf-1;i++){
    if(l[31-i] != r[31-i]){
    return false;}}return true;
// int rb = 31-a.suf-1; //因为相同的已经合并了,所以如果前面相同,最后一位肯定不同
// 不过再进行一次判断确认也可以,不影响结果
// return (l[31-i]^r[31-i])==1;
}int main(){
    list<ip> ips;int n;scanf("%d",&n);for(int i = 0;i<n;i++){
    ip a = create();ips.insert(ips.end(),a);}ips.sort(cmp);int i;for(list<ip>::iterator iter = ips.begin(),next;iter!=ips.end();){
    next = iter;next++;if(next==ips.end()) break;//如果cur已经是最后一个元素了就退出if(issub(*iter,*next))//如果有子集关系,一定next是iter的子集,因为大小关系已经按顺序匹配好了ips.erase(next);elseiter++;//查看下一个元素}for(list<ip>::iterator iter = ips.begin(),next;iter!=ips.end();){
    next = iter;next++;if(next==ips.end()) break;if(merge(*iter,*next)){
    ips.erase(next);iter->suf-=1;if(iter!=ips.begin())//如果cur的前面还有元素,则让cur--,因为可能有递归合并的可能{
    iter--;continue;}}iter++;//考虑下一个元素}for(list<ip>::iterator iter = ips.begin();iter!=ips.end();iter++){
    iter->show();}return 0;
}

在这里插入图片描述