UVa 1590 IP Networks(IP 網路)
Alex is administrator of IP networks. His clients have a bunch of individual IP addresses and he decided to group all those IP addresses into the smallest possible IP network.
Each IP address is a 4-byte number that is written byte-by-byte in a decimal dot-separated notation ``byte0.byte1.byte2.byte3" (quotes are added for clarity). Each byte is written as a decimal number from 0 to 255 (inclusive) without extra leading zeroes.
IP network is described by two 4-byte numbers - network address and network mask. Both network address and network mask are written in the same notation as IP addresses.
In order to understand the meaning of network address and network mask you have to consider their binary representation. Binary representation of IP address, network address, and network mask consists of 32 bits: 8 bits for byte0 (most significant to least significant), followed by 8 bits for byte1, followed by 8 bits for byte2, and followed by 8 bits for byte3.
IP network contains a range of 2n IP addresses where 0n32 .
Network mask always has 32 - n first bits set to one, and n last bits set to zero in its binary representation. Network address has arbitrary 32 - n first
bits, and n last bits set to zero in its binary representation. IP network contains all IP addresses whose 32 - n
For example, IP network with network address 194.85.160.176 and network mask 255.255.255.248 contains 8 IP addresses from 194.85.160.176 to 194.85.160.183 (inclusive).
Input
The input file will contain several test cases, each of them as described below.
The first line of the input file contains a single integer number m(1m1000) . The following m lines contain IP addresses, one address on a line. Each IP address may appear more than once in the input file.
For each test case, write to the output file two lines that describe the smallest possible IP network that contains all IP addresses from the input file. Write network address on the first line and network mask on the second line.
3 194.85.160.177 194.85.160.183 194.85.160.178
194.85.160.176 255.255.255.248
接到訓練題後翻了一遍,第一眼就瞅上這個題了,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,假設其子網掩碼二進位制有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和最大IP,然後找到這兩個IP的二進位制從左往右看哪一位最先出現不同(異或運算可解),就可以知道子網掩碼裡有幾個連續的1,自然就得到子網掩碼了,然後最小IP便迎刃而解。所以這個題其實很簡單,只要瞭解點IP地址相關知識即可。
實現方面,寫了個ip類,有四部分,分別對應點分十進位制的四部分,過載<運算子,sort排序後找到給定IP裡的最大和最小,這兩個IP每部分依次異或運算得到子網掩碼連續的1的個數,得到子網掩碼,與任意IP按位與,得到最小IP,個人認為這題的精華是位運算的應用,程式碼如下:
#include <cstdio>
#include <algorithm>
using namespace std;
struct ip //定義ip類,四個部分
{
int part[4];
ip() //預設構造方法,全為0
{
part[0]=part[1]=part[2]=part[3]=0;
}
void print() //把輸出定義成了成員函式
{
printf("%d.%d.%d.%d\n",part[0],part[1],part[2],part[3]);
}
bool operator < (const ip a)const //過載小於運算,方便用sort排序找到最大最小
{
for(int i=0;i<4;i++)
if(part[i]!=a.part[i])
return part[i]<a.part[i];
return false;
}
}add[1010];
int toBit(int n);
int getMinBit(ip a,ip b); //求子網掩碼連續1的位數
int main()
{
int n;
while(~scanf("%d",&n))
{
ip minn,mask;
for(int i=0;i<n;i++)
scanf("%d.%d.%d.%d",&add[i].part[0],&add[i].part[1],&add[i].part[2],&add[i].part[3]);
sort(add,add+n);
int bit=getMinBit(add[0],add[n-1]); //將IP最大和最小值傳入函式求出子網掩碼連續1的個數
int site=(bit-1)/8; //得到子網掩碼第一個0所在的部分(點分十進位制四部分)
for(int i=0;i<site;i++) //這部分之前的部分都是255(即11111111)
mask.part[i]=255;
int m=8-bit+(site*8); //m為第一個0所在部分那8位中有幾個0
mask.part[site]=(255>>m)<<m; //255(11111111)先右移m位再左移m位即可得8-m個1和m個0,經過此步即可得子網掩碼
for(int i=0;i<4;i++)
minn.part[i]=add[0].part[i]&mask.part[i]; //子網掩碼與任意IP每部分按位與運算得到子網內最小IP
minn.print();
mask.print();
}
return 0;
}
int toBit(int n) //給定一個十進位制整數,得到整數的二進位制位數
{
int res;
for(res=0;n;res++)
n/=2;
return res;
}
int getMinBit(ip a,ip b)
{
int res=32;
for(int i=0;i<4;i++)
if(a.part[i]!=b.part[i]) //對應部分不等,則找出二進位制位從第幾位開始出現不等
{
res=8-toBit(a.part[i]^b.part[i]); //異或運算,兩運算元對應二進位制位不等得1,反之得0,根據異或結果可知出現不等的最左位置
res+=(i*8); //得到子網掩碼連續1的個數
break;
}
return res;
}