题目来源
数据特点
题目提示
知识点
-
整数与字符串相互转换
<stdlib.h> 整数转字符串: itoa (表示 integer to alphanumeric)是把整型数转换成字符串的一个函数。 char* itoa(int value,char*string,int radix);// value: 要转换的整数,string: 转换后的字符串,radix: 转换进制数,如2,8,10,16 进制等。``` 字符转整数: atoi (表示 alphanumeric to integer)是把字符串转换成整型数的一个函数 int atoi(const char *nptr);//字符串转整数函数,nptr: 要转换的字符串
-
32位无符号整数: uint32_t
uint32_t 即 unsigned int https://blog.csdn.net/mary19920410/article/details/71518130 _t 表示这些数据类型是通过typedef定义的,而不是新的数据类型。也就是说,它们其实是我们已知的类型的别名。 typedef unsigned int uint32_t; typedef signed char int8_t; typedef short int int16_t; typedef int int32_t; typedef long int int64_t; typedef long long int int64_t;
0 分
只处理标准型IP前缀,且只进行排序。无合并
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h> // itoa,
#include <algorithm>
using namespace std;
vector<pair<int, int> > ips; // [{pref, len}, {pref, len}, ... ] pref: ip地址
bool cmp(pair<int, int> p1, pair<int, int> p2) {
if (p1.first != p2.first) { // 比较 ip地址/pref 大小
return p1.first < p2.first;
}
else { // 比较 len 大小
return p1.second < p2.second;
}
}
int main() {
ifstream cin("in3.txt");
int N; // N <= 10^6
cin>>N;
for (int i = 0; i < N; ++i) {
string cur;
cin>>cur; // 标准型、 省略后缀型、 省略长度型 !合法的!
// 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
// 标准型:先计算ip地址pref为十进制整数, 再计算 len;
// 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
// 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
int ip = 0;
int num = 0;
// 只处理标准型的ip
for (int j = 0; j < cur.size(); ++j) {
char c = cur[j];
if (c == '.'|| c == '/') {
ip = ip * 256 + num;
num = 0;
}
else {
num = num * 10 + c - '0';
}
}
ips.push_back(pair<int, int>{ip, num});
}
// 排序
sort(ips.begin(), ips.end(), cmp);
// 合并1
// 合并2
// ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
for (pair<int , int > ip: ips) {
// 转换
int a[4] = {0};
int indexa = 0;
int tmpip = ip.first;
while (tmpip != 0) {
a[indexa++] = tmpip % 256;
tmpip = tmpip / 256;
}
// 输出
cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
}
return 0;
}
40 分
就这20分来写的,得了40分。
将0分代码int改为32位无符号整数 uint32_t。
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h> // itoa,
#include <algorithm>
using namespace std;
vector<pair<uint32_t, int> > ips; // [{pref, len}, {pref, len}, ... ] pref: ip地址
bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
if (p1.first != p2.first) { // 比较 ip地址/pref 大小
return p1.first < p2.first;
}
else { // 比较 len 大小
return p1.second < p2.second;
}
}
int main() {
ifstream cin("in3.txt");
int N; // N <= 10^6
cin>>N;
for (int i = 0; i < N; ++i) {
string cur;
cin>>cur; // 标准型、 省略后缀型、 省略长度型 !合法的!
// 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
// 标准型:先计算ip地址pref为十进制整数, 再计算 len;
// 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
// 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
uint32_t ip = 0;
int num = 0;
// 只处理标准型的ip
for (int j = 0; j < cur.size(); ++j) {
char c = cur[j];
if (c == '.'|| c == '/') {
ip = ip * 256 + num;
num = 0;
}
else {
num = num * 10 + c - '0';
}
}
ips.push_back(pair<uint32_t, int>{ip, num});
}
// 排序
sort(ips.begin(), ips.end(), cmp);
// 合并1
// 合并2
// ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
for (pair<uint32_t , int > ip: ips) {
// 转换
int a[4] = {0};
int indexa = 0;
uint32_t tmpip = ip.first;
while (tmpip != 0) {
a[indexa++] = tmpip % 256;
tmpip = tmpip / 256;
}
// 输出
cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
}
return 0;
}
40分
添加 处理非标准型IP前缀的方法。
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h> // itoa,
#include <algorithm>
using namespace std;
vector<pair<uint32_t, int> > ips; // [{pref, len}, {pref, len}, ... ] pref: ip地址
bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
if (p1.first != p2.first) { // 比较 ip地址/pref 大小
return p1.first < p2.first;
}
else { // 比较 len 大小
return p1.second < p2.second;
}
}
int main() {
ifstream cin("in2.txt");
int N; // N <= 10^6
cin>>N;
for (int i = 0; i < N; ++i) {
string cur;
cin>>cur; // 标准型、 省略后缀型、 省略长度型 !合法的!
// 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
// 标准型:先计算ip地址pref为十进制整数, 再计算 len;
// 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
// 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
uint32_t ip = 0;
int len = 8;
string standard = "";
int dotnum = 0;
int xienum = 0;
int num = 0;
for (int j = 0; j < cur.size(); ++j) {
char c = cur[j];
if (c == '.') {
++dotnum; // 记录 '.' 数量
len += 8;
ip = ip * 256 + num;
num = 0;
}
else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) { // 遇到 '/' or 是省略长度型且只有一段
if (c == '/') {
++xienum;
}
else {
num = num * 10 + c - '0';
}
while (dotnum != 3) { // 补充ip地址中(3 - dotnum)的'.0'
ip = ip * 256 + num;
num = 0;
++dotnum;
}
ip = ip * 256 + num;
num = 0;
}
else {
num = num * 10 + c - '0';
}
}
if (xienum == 0) { // 省略长度型
ips.push_back(pair<uint32_t, int>{ip, len});
}
else {
ips.push_back(pair<uint32_t, int>{ip, num});
}
}
// 排序
sort(ips.begin(), ips.end(), cmp);
// 合并1
// 合并2
// ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
for (pair<uint32_t , int > ip: ips) {
// 转换
int a[4] = {0};
int indexa = 0;
uint32_t tmpip = ip.first;
while (tmpip != 0) {
a[indexa++] = tmpip % 256;
tmpip = tmpip / 256;
}
// 输出
cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
}
return 0;
}
80分
完成 排序、 从小到大合并
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <stdlib.h> // itoa,
#include <algorithm>
using namespace std;
bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
if (p1.first != p2.first) { // 比较 ip地址/pref 大小
return p1.first < p2.first;
}
else { // 比较 len 大小
return p1.second < p2.second;
}
}
bool isSubSet(uint32_t a, uint32_t b, int alen, int blen) { // ip前缀b是ip前缀a的子集
int aa[32] = {0}, bb[32] = {0}; // 存储a、b的二进制
uint32_t tmpa = a, tmpb = b;
int indexa = 31, indexb = 31;
while (tmpa != 0) {
aa[indexa--] = tmpa % 2;
//cout<<tmpa%2<<endl;
tmpa = tmpa / 2;
}
while (tmpb != 0) {
bb[indexb--] = tmpb % 2;
tmpb = tmpb / 2;
}
for (int i = 0; i < min(alen, blen); ++i) {
if (bb[i] != aa[i]) {
return false;
}
}
return true;
}
bool isOK(uint32_t aa, int aalen, uint32_t a, int alen, uint32_t b, int blen) {
int aa2[32] = {0}, a2[32] = {0}, b2[32] = {0}; // 存储aa的二进制
uint32_t tmpaa = aa, tmpa = a, tmpb = b;
int indexaa = 31, indexa = 31, indexb = 31;
// aa 是不是合法的
while (tmpaa != 0) {
aa2[indexaa--] = tmpaa % 2;
tmpaa = tmpaa / 2;
}
for (int i = aalen; i < 32; ++i) {
if (aa2[i] == 1) {
return false;
}
}
// 获取 a, b的二进制
while (tmpa != 0) {
a2[indexa--] = tmpa % 2;
tmpa = tmpa / 2;
}
while (tmpb != 0) {
b2[indexb--] = tmpb % 2;
tmpb = tmpb / 2;
}
// 判断 a + b == aa
if ((a2[aalen] == 0 && b2[aalen] == 1) || (a2[aalen] == 1 && b2[aalen] == 0)) {
return true;
}
else {
return false;
}
}
int main() {
ifstream cin("in4.txt");
int N; // N <= 10^6
cin>>N;
vector<pair<uint32_t, int> > ips; // [{pref, len}, {pref, len}, ... ] pref: ip地址
for (int i = 0; i < N; ++i) {
string cur;
cin>>cur; // 标准型、 省略后缀型、 省略长度型 !合法的!
// 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
// 标准型:先计算ip地址pref为十进制整数, 再计算 len;
// 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
// 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
uint32_t ip = 0;
int len = 8;
string standard = "";
int dotnum = 0;
int xienum = 0;
int num = 0;
for (int j = 0; j < cur.size(); ++j) {
char c = cur[j];
if (c == '.') {
++dotnum; // 记录 '.' 数量
len += 8;
ip = ip * 256 + num;
num = 0;
}
else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) { // 遇到 '/' or 是省略长度型且只有一段
if (c == '/') {
++xienum;
}
else {
num = num * 10 + c - '0';
}
while (dotnum != 3) { // 补充ip地址中(3 - dotnum)的'.0'
ip = ip * 256 + num;
num = 0;
++dotnum;
}
ip = ip * 256 + num;
num = 0;
}
else {
num = num * 10 + c - '0';
if (j == cur.size() - 1 && xienum == 0 && dotnum != 0) { // 省略长度型, 且不是一段
while (dotnum != 3) { // 补充ip地址中(3 - dotnum)的'.0'
ip = ip * 256 + num;
num = 0;
++dotnum;
}
ip = ip * 256 + num;
num = 0;
}
}
}
if (xienum == 0) { // 省略长度型
ips.push_back(pair<uint32_t, int>{ip, len});
}
else {
ips.push_back(pair<uint32_t, int>{ip, num});
}
}
// 排序
sort(ips.begin(), ips.end(), cmp);
// 从小到大合并,
pair<uint32_t, int> ips2[N];
int num_ips2 = 0;
int index1 = 0, index2 = 1;
while (index2 < N) {
uint32_t a = ips[index1].first, b = ips[index2].first;
int alen = ips[index1].second, blen = ips[index2].second;
if (isSubSet(a, b, alen, blen)) { // ip前缀b是ip前缀a的子集, 删除b, 往后移
if (index2 == N - 1) { // 到达最后一个ip了, 但是b是a的子集
ips2[num_ips2++] = ips[index1];
}
++index2;
}
else {
ips2[num_ips2++] = ips[index1];
if (index2 == N - 1) { // 到达最后一个ip了, b不是a的子集
ips2[num_ips2++] = ips[index2];
}
index1 = index2;
++index2;
}
}
// ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
for (int i = 0; i < num_ips2; ++i) { // <=newnum <num_ips2
pair<uint32_t, int> ip = ips2[i];
// 转换
int a[4] = {0};
int indexa = 0;
uint32_t tmpip = ip.first;
while (tmpip != 0) {
a[indexa++] = tmpip % 256;
tmpip = tmpip / 256;
}
// 输出
cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
}
return 0;
}
100分
添加了同级合并后, 总是30分, 后来发现是 isOK()函数中 判断a并b是否等于a'
时没写判断前len-1项是否相等了。
#include <iostream>
//#include <fstream>
#include <vector>
#include <map>
//#include <stdlib.h> // itoa,
#include <algorithm>
#include <stack>
using namespace std;
bool cmp(pair<uint32_t, int> p1, pair<uint32_t, int> p2) {
if (p1.first != p2.first) { // 比较 ip地址/pref 大小
return p1.first < p2.first;
}
else { // 比较 len 大小
return p1.second < p2.second;
}
}
// 判断ip前缀b是否是ip前缀a的子集
bool isSubSet(uint32_t a, uint32_t b, int alen, int blen) {
int aa[32] = {0}, bb[32] = {0}; // 存储a、b的二进制
uint32_t tmpa = a, tmpb = b;
int indexa = 31, indexb = 31;
while (tmpa != 0) {
aa[indexa--] = tmpa % 2;
tmpa = tmpa / 2;
}
while (tmpb != 0) {
bb[indexb--] = tmpb % 2;
tmpb = tmpb / 2;
}
for (int i = 0; i < min(alen, blen); ++i) {
if (bb[i] != aa[i]) {
return false;
}
}
return true;
}
// 判断 aa (即a')是否合法 以及 a并b是否等于 aa(即a')
bool isOK(uint32_t aa, int aalen, uint32_t a, int alen, uint32_t b, int blen) {
if (aalen < 0) {
return false;
}
int aa2[32] = {0}, a2[32] = {0}, b2[32] = {0}; // 存储aa的二进制
uint32_t tmpaa = aa, tmpa = a, tmpb = b; // 存储 32位无符号整型
int indexaa = 31, indexa = 31, indexb = 31; // 记录下标
// aa 是不是合法的
while (tmpaa != 0) {
aa2[indexaa--] = tmpaa % 2;
tmpaa = tmpaa / 2;
}
/*
// 改为下边的 if 更简单
for (int i = aalen; i < 32; ++i) {
if (aa2[i] == 1) {
return false;
}
}
*/
if (aa2[aalen] == 1) {
return false;
}
// 获取 a, b的二进制
while (tmpa != 0) {
a2[indexa--] = tmpa % 2;
tmpa = tmpa / 2;
}
while (tmpb != 0) {
b2[indexb--] = tmpb % 2;
tmpb = tmpb / 2;
}
// 判断 a 、 b 前 len - 1位是否相等 !!!! 这里给忘了!!!
for (int i = 0; i < aalen; ++i) {
if (a2[i] != b2[i]) {
return false;
}
}
// 判断 a + b == aa
if ((a2[aalen] == 0 && b2[aalen] == 1) || (a2[aalen] == 1 && b2[aalen] == 0) ) {
return true;
}
else {
return false;
}
}
int main() {
//ifstream cin("in4.txt");
int N; // N <= 10^6
cin>>N;
vector<pair<uint32_t, int> > ips; // [{pref, len}, {pref, len}, ... ] pref: ip地址
for (int i = 0; i < N; ++i) {
string cur;
cin>>cur; // 标准型、 省略后缀型、 省略长度型 !合法的!
// 处理 ip前缀为 [{32位无符号整数, length}, {}, ...]。
// 标准型:先计算ip地址pref为十进制整数, 再计算 len;
// 省略后缀型: 注意ip地址中(3 - dotnum)的'.0', 其余和标准型一致;
// 省略长度型: 注意ip地址中(3 - dotnum)的'.0' 和 '/len', 其余和标准型一致。
uint32_t ip = 0;
int len = 8;
string standard = "";
int dotnum = 0;
int xienum = 0;
int num = 0;
for (int j = 0; j < cur.size(); ++j) {
char c = cur[j];
if (c == '.') {
++dotnum; // 记录 '.' 数量
len += 8;
ip = ip * 256 + num;
num = 0;
}
else if (c == '/' || (j == cur.size() - 1 && dotnum == 0)) { // 遇到 '/' or 是省略长度型且只有一段
if (c == '/') {
++xienum;
}
else {
num = num * 10 + c - '0';
}
while (dotnum != 3) { // 补充ip地址中(3 - dotnum)的'.0'
ip = ip * 256 + num;
num = 0;
++dotnum;
}
ip = ip * 256 + num;
num = 0;
}
else {
num = num * 10 + c - '0';
if (j == cur.size() - 1 && xienum == 0 && dotnum != 0) { // 省略长度型, 且不是一段
while (dotnum != 3) { // 补充ip地址中(3 - dotnum)的'.0'
ip = ip * 256 + num;
num = 0;
++dotnum;
}
ip = ip * 256 + num;
num = 0;
}
}
}
if (xienum == 0) { // 省略长度型
ips.push_back(pair<uint32_t, int>{ip, len});
}
else {
ips.push_back(pair<uint32_t, int>{ip, num});
}
}
// 排序
sort(ips.begin(), ips.end(), cmp);
// 从小到大合并
pair<uint32_t, int> ips2[N];
int num_ips2 = 0;
int index1 = 0, index2 = 1;
while (index2 < N) {
uint32_t a = ips[index1].first, b = ips[index2].first;
int alen = ips[index1].second, blen = ips[index2].second;
if (isSubSet(a, b, alen, blen)) { // ip前缀b是ip前缀a的子集, 删除b, 往后移
if (index2 == N - 1) { // 到达最后一个ip了, 但是b是a的子集
ips2[num_ips2++] = ips[index1];
}
++index2;
}
else {
ips2[num_ips2++] = ips[index1];
if (index2 == N - 1) { // 到达最后一个ip了, b不是a的子集
ips2[num_ips2++] = ips[index2];
}
index1 = index2;
++index2;
}
}
// 同级合并 101.6.6.0/24 101.6.7.0/24
bool govector = true; //
stack<pair<uint32_t, int> > mystack;
int index = 1; // ips2的下标
mystack.push(ips2[0]);
while (index < num_ips2) {
if (govector) { // 推进ips2
uint32_t aa, a, b;
int aalen, alen, blen;
b = ips2[index].first;
blen = ips2[index].second;
aa = a = mystack.top().first;
alen = mystack.top().second;
aalen = alen - 1;
mystack.pop();
if (alen == blen && isOK(aa, aalen, a, alen, b , blen)) { // 前缀长度相同 且 能合并,合并后推进ips2/处理 mystack
mystack.push(pair<uint32_t, int>{aa, aalen}); // 合并
if (mystack.size() == 1) { // 前边没有元素, 推进 ips2
++index;
govector = true;
//cout<<"*1"<<endl;
}
else { // 前边有元素, 处理 mystack
++index;
govector = false;
//cout<<"*2"<<endl;
}
}
else { // 不能合并, 推进 ips2
++index;
govector = true;
mystack.push(pair<uint32_t, int>{a, alen});
mystack.push(pair<uint32_t, int>{b, blen}); // 合并
//cout<<"*3"<<endl;
}
}
else { // 处理 mystack
uint32_t aa, a, b;
int aalen, alen, blen;
b = mystack.top().first;
blen = mystack.top().second;
mystack.pop();
aa = a = mystack.top().first;
alen = mystack.top().second;
aalen = alen - 1;
mystack.pop();
if (alen == blen && isOK(aa, aalen, a, alen, b , blen)) { // 前缀长度相同 且 能合并, 合并后往前走/往后走
mystack.push(pair<uint32_t, int>{aa, aalen}); // 合并
if (mystack.size() == 1) { // 前边没有元素,推进 ips2
govector = true;
//cout<<"*4"<<endl;
}
else { // 前边有元素, 处理 mystack
govector = false;
//cout<<"*5"<<endl;
}
}
else { // 不能合并, 推进 ips2
govector = true;
mystack.push(pair<uint32_t, int>{a, alen});
mystack.push(pair<uint32_t, int>{b, blen}); // 合并
//cout<<"*6"<<endl;
}
}
}
// 从stack中获得答案
int anssize = mystack.size();
pair<uint32_t, int> ans[anssize];
for (int i = anssize - 1; i >= 0; --i) {
ans[i] = mystack.top();
mystack.pop();
}
// ip由 {pref(整数), len} 变为 a3.a2.a1.a0/len ; 输出答案。
for (int i = 0; i < anssize; ++i) { // <=newnum <num_ips2
pair<uint32_t, int> ip = ans[i];
// 转换
int a[4] = {0};
int indexa = 0;
uint32_t tmpip = ip.first;
while (tmpip != 0) {
a[indexa++] = tmpip % 256;
tmpip = tmpip / 256;
}
// 输出
cout<<a[3]<<'.'<<a[2]<<'.'<<a[1]<<'.'<<a[0]<<'/'<<ip.second<<endl;
}
return 0;
}