CCF计算机软件能力认证(CSP)部分题解

201812-3 CIDR合并

题目想求与给定前缀列表等价的包含IP前缀数目最少的前缀列表。

首先是怎么存储前缀列表。用一个long long存储IP地址,再存一个前缀长度,封装在一个结构体里\(<ipNum, len>\),方便后面排序等操作。IP前缀有三种输入格式,稍微分情况讨论一下。

接着以\(ipNum\)为第一关键字,\(len\)为第二关键字升序排序。

然后考虑去除匹配集被其它IP前缀包含的IP前缀。考虑之前匹配集范围的上届\(mmax\),顺序遍历一下就好了。将剩余的IP列表按之前顺序存在一个静态链表中。

最后将相邻的可合并的IP前缀合并,其实就是前缀长度最后一位0和1,之前完全相同即可。

温习一下链表的插入删除操作。

#include <bits/stdc++.h>
typedef long long LL;
const int maxn = 1000000;

using namespace std;

struct tIP
{
    LL ipNum;
    int len;
    int before, next;
    tIP()
    {
        before = next = -1;
    }
    bool operator < (const tIP &y) const
    {
        if(ipNum == y.ipNum)
            return len < y.len;
        return ipNum < y.ipNum;
    }
    void show()
    {
        LL num[5];
        LL temp = ipNum;
        for (int i = 4; i >= 1; i--)
        {
            num[i] = temp % 256;
            temp /= 256;
        }
        for (int i = 1; i <=4; i++)
        {
            printf("%lld", num[i]);
            if (i == 4)
                printf("/");
            else
                printf(".");
        }
        printf("%d\n", len);
    }
};
tIP ip[maxn+10];

LL getMMax(tIP iip)
{
    LL temp = (1LL << (32-iip.len)) - 1;
    return iip.ipNum | temp;
}

int main()
{
    int n;
    scanf("%d", &n);

    char s[30];
    for (int id = 1, slash, dotCnt, style; id <= n; id++)
    {
        slash = 0;
        dotCnt = 0;
        scanf("%s", s + 1);
        for (int i = 1; s[i] != '\0'; i++)
        {
            if (s[i] == '/')
                slash = 1;
            if (s[i] == '.')
                dotCnt ++;
        }

        if (slash == 1 && dotCnt == 3)
            style = 1;
        else if (slash == 1 && dotCnt < 3)
            style = 2;
        else
            style = 3;

        LL num[5];
        memset(num, 0, sizeof(num));
        if (style == 1 || style == 2)
        {
            for (int i = 1, temp = 0, numCnt = 1; ; i++)
            {
                if (s[i] == '.' || s[i] == '/')
                {
                    num[numCnt++] = temp * 1LL;
                    temp = 0;
                }
                else if (s[i] == '\0')
                {
                    ip[id].len = temp;
                    break;
                }
                else
                {
                    temp = temp * 10 + s[i] - '0';
                }
            }
        }
        else
        {
            for (int i = 1, temp = 0, numCnt = 1; ; i++)
            {
                if (s[i] == '.')
                {
                    num[numCnt++] = temp * 1LL;
                    temp = 0;
                }
                else if (s[i] == '\0')
                {
                    num[numCnt++] = temp * 1LL;
                    ip[id].len = (numCnt-1) * 8;
                    break;
                }
                else
                {
                    temp = temp * 10 + s[i] - '0';
                }
            }
        }
        LL ans = 0;
        for (int i = 1; i <= 4; i++)
        {
            ans = ans * 256 + num[i];
        }
        ip[id].ipNum = ans;
    }

    sort(ip + 1, ip + 1 + n);

    LL mmax = -1;
    int st = 0, en = n + 1;
    ip[st].before = -1;
    ip[st].next = en;
    ip[en].before = st;
    ip[en].next = -1;
    for (int id = 1, prev = 0; id <= n; id++)
    {
        if (ip[id].ipNum > mmax)
        {
            ip[id].before = prev;
            ip[id].next = en;
            ip[prev].next = ip[en].before = id;
            prev = id;
            mmax = getMMax(ip[id]);
        }
    }

    int pNow = ip[0].next;
    while (pNow != en)
    {
        int p1 = pNow, p2 = ip[pNow].before;
        if (p2 == 0)
            pNow = ip[pNow].next;
        else
        {
            if (ip[p1].len == ip[p2].len &&
                (ip[p2].ipNum & (1LL << (32-ip[p2].len))) == 0 &&
                (ip[p2].ipNum | (1LL << (32-ip[p2].len))) == ip[p1].ipNum)
            {
                ip[p1].before = ip[p2].before;
                ip[ip[p1].before].next = p1;
                ip[p1].ipNum = ip[p2].ipNum;
                ip[p1].len --;
            }
            else
            {
                pNow = ip[pNow].next;
            }
        }
    }

    pNow = ip[0].next;
    while (pNow != en)
    {
        ip[pNow].show();
        pNow = ip[pNow].next;
    }

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/acboyty/p/11229719.html