201812 CSP认证 | CIDR合并

本文详细描述了一种使用C++编程语言实现的算法,通过位运算和排序技术,解决网络CIDR地址的合并问题,重点在于理解如何判断地址的匹配性和进行有效的合并操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CIDR合并
难是真的不难但是也写了我几个小时服了
这道题在有计网的基础上就很好理解了,没有在格式上有任何刁难你的。这里不讲背景了
官网提交结果以及满分代码如下:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<string, int> PSI;
const int N = 1e5 + 10;
int n;
struct node{
    string ip;
    int len;
    ll ten;  //ip前缀的十进制表示:为了方便排序
};
vector<node> ipPool;
vector<node> merge1; //第一轮合并的结果
vector<node> res;   //同级合并的最终结果
int to_int(string str)
{
    stringstream ssin(str);
    int x;
    ssin >> x;
    return x;
}
string to_str(int m)
{
    stringstream ssin;
    ssin << m;
    string str;
    ssin >> str;
    return str;
}
//用于第一步排序
bool cmp(node x, node y)
{
    if(x.ten == y.ten) return x.len < y.len;
    else return x.ten < y.ten;
}
//处理一个输入的ip,将其处理后存储到ipPool中
void handle(string str)
{
    int k = str.find('/');
    int len = 0; node temp;
    if(k != -1)  { len = to_int(str.substr(k + 1)); str = str.substr(0, k); }

    //开始解析ip, 将字符串按照'.'分割开来
    vector<string> vec;
    for(int i = 0, j = 0; i < str.size(); i = j + 1){  //将str用'+'分割开来
        j = str.find('.', i);  //从下标i开始找+号
        if(j == -1) j = str.size();
        vec.push_back(str.substr(i, j - i));
    }
    if(!len) len = vec.size() * 8;  //省略了长度型的ip书写方式

    if(vec.size() != 4){
        int sz = vec.size();
        for(int i = 1;i <= 4 - sz;i ++){
            vec.push_back("0");  //补0
        }
    }
    string ip = ""; ll ten = 0;  //将ip转化为十进制的数字
    for(int i = 0;i < vec.size();i ++) {
        ip = ip + vec[i] + '.';
        ll x = to_int(vec[i]);
        x <<= ((3 - i) * 8);
        ten += x;
    }

    ip = ip.substr(0, ip.size() - 1); //删去最后一个点
    ip += "/" + to_str(len);

    temp.ip = ip; temp.len = len; temp.ten = ten;
    ipPool.push_back(temp);
}

//判断ipPool中b位置ip的匹配集是否是a位置ip匹配集的子集
bool subset(int a, int b)
{
    int lena = ipPool[a].len, lenb = ipPool[b].len;
    if(lena > lenb) return false;

    //接下来是lena <= lenb的情况,也就是二者的前lena个bit位置必须相同
    int delta = 32 - lena;
    int tena = ipPool[a].ten >> delta, tenb = ipPool[b].ten >> delta;  //只保留前lena位

    int temp = tena ^ tenb;
    if(temp) return false;  //前lena个bit位不相同
    return true;
}
//将ipPool中的元素从小到大进行合并
void small_Large_Merge()
{
    int a = 0, b = 1;
    bool st[N]; //考虑某个位置的元素是否存在
    memset(st, true, sizeof st);
    while(b < n){    //n也是ipPool的大小
        if(subset(a, b)){ st[b] = false; b ++; }
        else{ a = b; b ++; }
    }

    for(int i = 0;i < n;i ++){
        if(st[i]) merge1.push_back(ipPool[i]);
    }
}
//对merge1进行同级合并
void final_Merge()
{
    int a = 0, b = 1, sz = merge1.size();
    bool st[N];
    memset(st, true, sizeof st);  //功能同前面相同
    while(b < sz){
        node t;  //存储中间结果
        int lena = merge1[a].len, lenb = merge1[b].len;
        bool flag = false;  //是否能合并
        if(lena == lenb){
            //看二者是否能合并, 也就是看前len - 1位置是否完全相同,且最后一位不同
            int delta = 32 - lena;
            int tena = merge1[a].ten >> delta, tenb = merge1[b].ten >> delta;
            int tena2 = merge1[a].ten >> (delta + 1), tenb2 = merge1[b].ten >> (delta + 1);
            int cnt = 0;   //用来记录前len位有几位不相同
            int temp = tena ^ tenb;
            while(temp){
                cnt ++;
                temp &= (temp - 1);
            }

            temp = tena2 ^ tenb2;
            if(!temp && cnt == 1){  //前len - 1相同,第len不同,可以合并
                flag = true;
                t.ten = merge1[a].ten;
                t.len = merge1[a].len - 1; //长度不同
                string ip = merge1[a].ip;
                int k = ip.find('/');
                ip = ip.substr(0, k + 1);
                ip += to_str(t.len);
                t.ip = ip;
            }
        }
        if(flag){
            merge1[a] = t; st[b] = false;  //更新; a向前回溯
            if(a > 0){   //a > 0才有回溯的必要,注意这里st[0]是不可能为false的
                b = a; a --;
                while(!st[a]) a --;
            }
            else { //无需向前找了
                while(b < sz && !st[b]) b ++;
            };
        }
        else {
            a = b;
            b ++;
            while(b < sz && !st[b]) b ++;
        }
    }
    for(int i = 0;i < sz;i ++){
        if(st[i]) res.push_back(merge1[i]);
    }
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);

    cin >> n;
    for(int i = 0;i < n;i ++){
        string ip; cin >> ip;
        handle(ip);
    }
    sort(ipPool.begin(),ipPool.end(), cmp);

    //现在开始从小到大合并
    small_Large_Merge();
    //同级合并
    final_Merge();
    for(auto x : res){
        cout << x.ip << endl;
    }

    return 0;
}

本题我用了位运算来实现。能否合并的关键就是看前几位是否是相同的,如果前x位相同,第x + 1的异或结果为1的话,此时两个就可以合并成一个。因此我设置了一个len来保存ip地址的十进制数。
但就是这个我改了很久的错误
第62行我的初始代码是:ten += ( x << ((3 - i) * 8) ),结果总是负数
后来又测试了几个如下:
在这里插入图片描述
其实就是符号的问题。
如果我直接对128移位置,只在低位补零;此时的x的表示形式就是10000000 0000000 0000000 0000000。从补码的角度来说,因为最高位为1,此时就是负数!!!
而我若先赋值为128,此时这个数据只是存在与x空间的存储形式为00000000 00000000 00000000 10000000,再移位;此时变为00000000 10000000 0000000 0000000 0000000最高位仍然为0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值