ccf计算机软件能力认证考试难度,CCF计算机软件能力认证(CSP)部分题解

201812-3 CIDR合并

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

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

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

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

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

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

#include

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;

}

原文:https://www.cnblogs.com/acboyty/p/11229719.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值