信息学奥赛一本通 1415:【17NOIP普及组】图书管理员 | 洛谷 P3955 [NOIP2017 普及组] 图书管理员

【题目链接】

ybt 1415:【17NOIP普及组】图书管理员
洛谷 P3955 [NOIP2017 普及组] 图书管理员

【题目考点】

1. 枚举

2. 二分

【解题思路】

解法1:枚举

对于每个读者,需求码长度为len,需求码为code,图书编码存入book数组。
枚举所有图书编码,求满足该读者需求的图书中图书编码的最小值。
这里可以预处理出一个数组p,p[i] 1 0 i 10^i 10i
求数值x末len位的方法:x%p[len]

  • 枚举对象:图书编码
  • 枚举范围:所有的图书
  • 判断条件:判断图书编码末len位与读者需求码code是否相同。
    求满足该读者需求的图书编码中的最小值。

复杂度: O ( q ⋅ n ) O(q\cdot n) O(qn)

解法2:排序+顺序查找

可以先对book数组进行升序排序,而后对于每个读者,在book数组中顺序查找满足图书编码末len位与读者需求码code相同的第一个图书编码,该编码就是满足条件的图书编码中的最小值。
复杂度: O ( q ⋅ n ) O(q\cdot n) O(qn)

解法3:排序+二分查找

可以先对book数组进行升序排序,而后对于每个读者,在book数组中二分查找满足图书编码末len位与读者需求码code相同的第一个图书编码。这就要求图书编码关于末len位有序(如果末len位相同,那么图书编码小的排在前)。
题目要求图书编码不超过 1 0 7 10^7 107,因此对图书编码分别按照:末1位,末2位,…,末7位升序进行7次排序,得到7个有序序列。按照末i位排序得到的有序序列为b[i]

如果要找满足图书编码末len位与读者需求码code相同的第一个图书编码,那么就在b[len]数组中二分查找末len位大于等于code的最小值。如果该最小值末len位与code相同,那么输出该最小值,否则输出-1。

得到7个有序序列b[1]~b[7]的复杂度为 O ( q ⋅ l o g q ) O(q\cdot log q) O(qlogq)
对于每个读者查找图书编码的复杂度为 O ( n ⋅ l o g q ) O(n\cdot log q) O(nlogq)
整体复杂度: O ( ( q + n ) ⋅ l o g ( q ) ) O((q+n)\cdot log(q)) O((q+n)log(q))

【题解代码】

解法1:枚举

#include <bits/stdc++.h>
using namespace std;
#define N 1005
#define INF 0x3f3f3f3f
int book[N], p[10];//p[i]:10^i的值
void initP()
{
    
    
	p[0] = 1;
	for(int i = 1; i <= 7; ++i)//图书编码和需求码都不大于10^7,所以最大用到10^7 
		p[i] = p[i-1]*10;
}
int main()
{
    
    
	initP();
	int n, q, len, code;
	cin >> n >> q;
	for(int i = 1; i <= n; ++i)
		cin >> book[i];//book[i]:第i本书的图书编码 
	for(int i = 1; i <= q; ++i)
	{
    
    
		cin >> len >> code;
		int minCode = INF;
		for(int j = 1; j <= n; ++j)
			if(book[j]%p[len] == code)
				minCode = min(minCode, book[j]);
		cout << (minCode == INF ? -1 : minCode) << endl;
	}
	return 0;
} 

解法2:排序+顺序查找

#include <bits/stdc++.h>
using namespace std;
#define N 1005
int book[N], p[10];//p[i]:10^i的值
void initP()
{
    
    
	p[0] = 1;
	for(int i = 1; i <= 7; ++i)//图书编码和需求码都不大于10^7,所以最大用到10^7 
		p[i] = p[i-1]*10;
}
int main()
{
    
    
	initP();
	int n, q, len, code;
	cin >> n >> q;
	for(int i = 1; i <= n; ++i)
		cin >> book[i];//book[i]:第i本书的图书编码 
	sort(book+1, book+1+n);//升序排序 
	for(int i = 1; i <= q; ++i)
	{
    
    
		cin >> len >> code;
		bool isFound = false;
		for(int j = 1; j <= n; ++j)
		{
    
    
			if(book[j]%p[len] == code)
			{
    
    
				cout << book[j] << endl;
				isFound = true;
				break;
			}
		}
		if(isFound == false)
			cout << -1 << endl;
	}
	return 0;
} 

解法3:排序+二分查找

  • 写法1:设多个函数 手写二分查找
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int n, q, b[8][N], book[N], p[10];//p[i]:10^i的值 b[i]:book按照cmpi比较得到的有序序列 
void initP()
{
    
    
	p[0] = 1;
	for(int i = 1; i <= 7; ++i)//图书编码和需求码都不大于10^7,所以最大用到10^7 
		p[i] = p[i-1]*10;
}
//cmpl:a、b的末l位先比较,小的排在前。如果相等,a、b中较小的排在前。 
bool cmp1(int a, int b)
{
    
    
	return a%10 == b%10 ? a < b : a%10 < b%10;
}
bool cmp2(int a, int b)
{
    
    
	return a%100 == b%100 ? a < b : a%100 < b%100;
} 
bool cmp3(int a, int b)
{
    
    
	return a%1000 == b%1000 ? a < b : a%1000 < b%1000;
}  
bool cmp4(int a, int b)
{
    
    
	return a%10000 == b%10000 ? a < b : a%10000 < b%10000;
}  
bool cmp5(int a, int b)
{
    
    
	return a%100000 == b%100000 ? a < b : a%100000 < b%100000;
}  
bool cmp6(int a, int b)
{
    
    
	return a%1000000 == b%1000000 ? a < b : a%1000000 < b%1000000;
}  
bool cmp7(int a, int b)
{
    
    
	return a%10000000 == b%10000000 ? a < b : a%10000000 < b%10000000;
}  
void initB()
{
    
    
	for(int i = 1; i <= 7; ++i)
		memcpy(b[i], book, sizeof(book));//先复制book到b[i]
	sort(b[1]+1, b[1]+1+n, cmp1); 
	sort(b[2]+1, b[2]+1+n, cmp2);
	sort(b[3]+1, b[3]+1+n, cmp3);
	sort(b[4]+1, b[4]+1+n, cmp4);
	sort(b[5]+1, b[5]+1+n, cmp5);
	sort(b[6]+1, b[6]+1+n, cmp6);
	sort(b[7]+1, b[7]+1+n, cmp7);
}
int main()
{
    
    
	initP();
	int len, code;
	cin >> n >> q;
	for(int i = 1; i <= n; ++i)
		cin >> book[i];//book[i]:第i本书的图书编码 
	initB(); 
	for(int i = 1; i <= q; ++i)
	{
    
    
		cin >> len >> code;
		int l = 1, r = n, m;//二分查找b[len]中模p[len]后大于等于code的最小值 
		while(l < r)
		{
    
    
			m = (l+r)/2;
			if(b[len][m]%p[len] >= code)
				r = m;
			else
				l = m+1;
		}
		if(b[len][l]%p[len] == code)//如果找到的图书编号b[len][mi]的末len位就是要找的code 
			cout << b[len][l] << endl;
		else
			cout << -1 << endl;
	}
	return 0;
} 
  • 写法2:使用lambda表达式,lower_bound函数
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int n, q, b[8][N], book[N], p[10];//p[i]:10^i的值 b[i]:book按照cmpi比较得到的有序序列 
void init()
{
    
    
	p[0] = 1;
	for(int i = 1; i <= 7; ++i) 
	{
    
    
		p[i] = p[i-1]*10;//图书编码和需求码都不大于10^7,所以最大用到10^7
		memcpy(b[i], book, sizeof(book));//复制book到b[i]
		sort(b[i]+1, b[i]+1+n, 
			[i](int a, int c)//用lambda表达式来写比较函数,捕获使用外面的变量i 
			{
    
    
				return a%p[i] == c%p[i] ? a < c : a%p[i] < c%p[i];
			}
		);
	}
}
int main()
{
    
    
	int len, code;
	cin >> n >> q;
	for(int i = 1; i <= n; ++i)
		cin >> book[i];//book[i]:第i本书的图书编码 
	init();
	for(int i = 1; i <= q; ++i)
	{
    
    
		cin >> len >> code;
		int m = lower_bound(b[len]+1, b[len]+1+n, code, [len](int a, int c){
    
    return a%p[len] < c%p[len];}) - b[len];
		if(b[len][m]%p[len] == code)//如果找到的图书编号b[len][mi]的末len位就是要找的code 
			cout << b[len][m] << endl;
		else
			cout << -1 << endl;
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/128742849