问题描述
试题编号: | 201812-3 |
试题名称: | CIDR合并 |
时间限制: | 1.0s |
内存限制: | 512.0MB |
问题描述: |
样例输入 2 样例输出 1.0.0.0/8 样例输入 2 样例输出 10.0.0.0/8 样例输入 2 样例输出 0.0.0.0/0
|
最小ip地址:l=ip1.val>>(32-ip1.a[4])<<(32-ip1.a[4]); //前len位不变,后边跟32-len个0。即ip1先右移32-len位,再左移32-len位,即可将原ip前缀的后32-len位置零
最大ip地址:r=ip1.val|((1ull<<(32-ip1.a[4]))-1); //前len位不变,后边跟32-len个1 。
例:
10.128.1.1/9
原地址:0000 1010 1000 0000 0000 0001 0000 0001
最小: 0000 1010 1000 0000 0000 0000 0000 0000
最大: 0000 1010 1111 1111 1111 1111 1111 1111
求最小的过程:
求最大的过程:
求出a的匹配集的最小最大元素la,ra
b的匹配集的最小最大元素lb,rb
res的匹配集的最小最大元素lres,rres
对于 [la,ra]∪[lb,rb],只要ra+1>=lb,这两个匹配集就可以合起来,而不是ra>=lb才能合起来。因为是包含符],不是不包含符)
所以最后判断的条件是 la==lres && rb==rres && lb<=ra+1 而不是 la==lres && rb==rres && lb<=ra
虽然题目里已经给出来思路了,还是觉得好复杂一个题
100分代码
参考自https://blog.csdn.net/wingrez/article/details/86601755
#include
#include
#include
#include
#define MAXN 1000005
using namespace std;
typedef unsigned int UI;
struct IP{
UI a[5]; //点分十进制的形式 ,前四位是ip地址,最后一位是前缀长度
UI val;
};
int n;
IP ip[MAXN];
bool cmp(const IP &ip1, const IP &ip2){
for(int i=0;i<5;i++){
if(ip1.a[i]!=ip2.a[i]) return ip1.a[i]0){
p2=strchr(p1,'/');
strncpy(tmp,p1,p2-p1);
tmp[p2-p1]=0;
ip[no].a[cnt++]=atoi(tmp);
p1=p2+1;
strcpy(tmp,p1); //复制字符串from 中的字符到字符串to,包括空值结束符。返回值为指针to。
ip[no].a[4]=atoi(tmp);
}
else{
strcpy(tmp,p1);
ip[no].a[cnt++]=atoi(tmp);
ip[no].a[4]=cnt*8;
}
}
void countVal(int no){ //计算十进制值
ip[no].val=0;
for(int i=0;i<4;i++){
ip[no].val+=ip[no].a[i]<<8*(3-i);
}
}
void range(IP &ip,UI &l, UI &r){ //计算IP前缀能表示的数值范围
l=ip.val>>(32-ip.a[4])<<(32-ip.a[4]);
r=ip.val|((1ull<<(32-ip.a[4]))-1); // 1ull先移位(32-len),为100000……,再-1,为011111……,与原ip地址或,得到右边界
}
void union1(){ //从小到大合并
int p=0;
UI la,ra,lb,rb;
for(int i=1;i0 && judge_union(ip[p-1], ip[p], res)) ip[--p]=res;
}
else ip[++p]=ip[i];
}
n=p+1;
}
int main(){
char str[25];
scanf("%d",&n);
for(int i=0;i
自己重新敲的一遍
#include
#include
#include
#include
using namespace std;
typedef unsigned int UI;
int n;
struct IP{
UI a[5]; //存储具体的地址和前缀长度
UI val;
}ip[100005];
void deal_s(int no,char *s){
int len=strlen(s);
int pnum=0,onum=0; //pnum小数点的数量 ,onum斜杠的数量
for(int i=0;i0){
p2=strchr(p1,'/');
strncpy(temp,p1,p2-p1); //将p1中的前(p2-p1)个字符复制到temp中
temp[p2-p1]=0; //结束符,从str 中读到非数字字符则结束转换并将结果返回
ip[no].a[cnt++]=atoi(temp); //注意到这一步才把点分十进制的ip地址放完
p1=p2+1;
strcpy(temp,p1); //复制字符串from 中的字符到字符串to,包括空值结束符。返回值为指针to。
ip[no].a[4]=atoi(temp);
}
else{ //无'/',前缀长度就需要算出来
strcpy(temp,p1);
ip[no].a[cnt++]=atoi(temp);
ip[no].a[4]=8*(pnum+1);
}
//ip地址的值
UI value=0;
for(int i=0;i<4;i++){
value<<=8;
value+=ip[no].a[i];
}
ip[no].val=value;
}
bool cmp(IP ip1,IP ip2){
for(int i=0;i<5;i++){
if(ip1.a[i]==ip2.a[i]) continue;
return ip1.a[i]>(32-ip1.a[4])<<(32-ip1.a[4]); //前len位不变,后边跟32-len个0
r=ip1.val|((1ull<<(32-ip1.a[4]))-1); //前len位不变,后边跟32-len个1
}
void union1(){ //从小到大合并
int p=0;
UI la,ra,lb,rb;
for(int i=1;i=rb)) ip[++p]=ip[i];
}
n=p+1; //已经有一部分合并了
}
bool ifunion(IP ip1,IP ip2,IP &res){
if(ip1.a[4]!=ip2.a[4]) return false;
//新的ip前缀,ip地址与a相同,前缀长度比a小1
res=ip1;
res.a[4]-=1;
if(res.a[4]<0) return false; //还要判断-1后是否合法,即ip1是否有上一级
UI la,ra,lb,rb,lres,rres;
range(ip1,la,ra);
range(ip2,lb,rb);
range(res,lres,rres);
if(la==lres && rb==rres && lb<=ra+1){ //ip1的匹配集与ip2的匹配集的并集=res的匹配集
return true;
}
return false;
}
void union2(){ //同级合并
int p=0;
UI la,ra,lb,rb;
IP res;
for(int i=1;i0 && ifunion(ip[p-1],ip[p],res)){ //p前有元素,从p的前一个元素开始考虑
ip[--p]=res;
}
}
else{ //不能合并
ip[++p]=ip[i];
}
}
n=p+1;
}
int main(){
scanf("%d",&n);
char str[25];
for(int i=0;i