今天刚好考完计算机网络,刚好复习下IP和子网掩码的相关知识。
子网掩码:
子网掩码是子网划分的依据,它跟IP地址一样,长度也是32位,点分十进制表示,每部分0~255,但是跟IP地址不同的是,子网掩码只能由连续的1和0组成,也就是说,把这32位从任意位置分开,左边只能全是1,右边只能全是0。比如11111111.11111111.11111111.11111000(255.255.255.248)就是合法的子网掩码,而11000000.10101000.00000001.00000000(192.168.1.0)就不合法。
题意:
给定多个ip,求出这些ip的子网掩码,和在这个子网掩码下的最有效的ip,其实就是最小ip。
注意给的多个ip已是同一网段下,所以有效ip的前面就是这些ip的共同值,后面全是0.
思路:
给定两个IP,假设其子网掩码二进制有x个连续的1,则如果这两个IP的二进制前x位对应相等,那么这两个IP就属于同一网段,也就是属于同一个子网。
如果给定一个子网掩码和一个IP,就可以求出这个IP所在子网的最小IP,方法是将IP的二进制与子网掩码的二进制进行按位与运算,原理是,子网掩码为1的二进制位,要求子网内所有IP的这一位必须全部相等,而子网掩码为0的位不作要求,也就是说,给定一个IP,子网内最小IP对应的子网掩码为1的位必须跟给定IP一样,按位与的时候,给定IP与子网掩码是1的位按位与后的结果不变,子网掩码0的位按位与后为0(恰好是最小),这样按位与运算结束后,得到的IP就是子网内最小IP
根据上面所说,这个题只要求出子网掩码,然后与给定的任意IP进行按位与运算,就可以得到最小IP了。那么现在关键就是求子网掩码了,既然给定的这一些IP都是一个网段的,那么找到这些IP里的最小IP和最大IP,然后找到这两个IP的二进制从左往右看哪一位最先出现不同(异或运算可解),就可以知道子网掩码里有几个连续的1,自然就得到子网掩码了,然后最小IP便迎刃而解。所以这个题其实很简单,只要了解点IP地址相关知识即可。
#include <bits/stdc++.h> using namespace std; const int maxip = (1<<10)+10; //对应11111111,11111110,11111100,11111000,11110000,....10000000,00000000依次减去2^0,1,2,3 int tmp[10] = {255, 254, 252, 248, 240, 224, 192, 128, 0}; int main() { freopen("in.txt","r",stdin); // freopen("ip.out","w",stdout); int ip[4][maxip]; int n; while(~scanf("%d",&n)) { memset(ip, 0, sizeof(ip)); int dns[4]; int ansip[4]; for(int i=0; i<n; i++) scanf("%d.%d.%d.%d",&ip[0][i], &ip[1][i], &ip[2][i], &ip[3][i]); for(int i=0; i<4; i++) { int dif_cur=0, x, j; sort(ip[i], ip[i]+n); int Max = ip[i][n-1]; int Min = ip[i][0]; //这里可以优化 for(j=1; j<=8; j++) { if(Max%2 != Min%2) dif_cur = j; //从低位向高位移动,在每位判断当前位是否相同 Max = Max>>1; Min = Min>>1; } //求出这一段的子网掩码 dns[i] = tmp[dif_cur]; //最小IP //由这一段的子网掩码随便对一个ip(反正都是一个网段)与或运算,只有与1得1,与0会得ip原来的数位即0 //注意子网掩码和最小的ip不同就是ip在这个网段下,即这个网段的前x个数位都是相同的,子网掩码在这前x个都是1 ansip[i] = dns[i] & ip[i][1];//这个ip是随便的,只要是这一段的就行,即一维是i } for(int i=0; i<4; i++) { if(dns[i] != ((1<<8)-1) ) { for(i = i+1; i<4; i++) { dns[i] = 0; dns[i] = 0; } break; } } printf("%d.%d.%d.%d ",ansip[0], ansip[1], ansip[2], ansip[3]); printf("%d.%d.%d.%d ", dns[0], dns[1], dns[2], dns[3]); } return 0; }