目录
递归和分治 (Recursion and Divide and Conquer)
排序和优先队列 (Sorting and Priority Queue)
实用类数据结构 (Pratical Data Structure)
--------------------------------------------------------------------------------------------------------
-
头文件及宏定义
-
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <string> #include <stack> #include <queue> #include <set> #include <map> using namespace std; #define IOS ios::sync_with_stdio(0), cin.tie(0); typedef long long ll;
-
基础
18104 练习使用多case解题
时间限制:1000MS 代码长度限制 : 10KB
Description
多CASE的问题在般有3种情形:(1)有一个数字开始表明CASE数目;(2)以特殊标志表示结束;(3)要求处理到最后一行。
现要求你在程序一次运行中,依次处理上述3种情况。
有三批测试数据,第1批测试数据,开头会以一个数字告之该批CASE数量,每一个CASE是两个正整数;
第1批测试数据结束后,紧接着是第2批数据,每一个CASE同样是两个正整数,第2批测试数据以两个0结束;
第2批测试数据结束后,紧接着是第3批数据,每一个CASE也是两个正整数,第3批测试数据一直到数据输入结束;
要求,每一个CASE,输出两数的最小公倍数
第1批测试数据处理完毕时,输出“group 1 done”
第2批测试数据处理完毕时,输出“group 2 done”
第3批测试数据处理完毕时,输出“group 3 done”
输入格式
有三批测试数据,第1批测试数据,开头会以一个数字告之该批CASE数量,每一个CASE是两个正整数(最大2的31次方);
第1批测试数据结束后,紧接着是第2批数据,每一个CASE同样是两个正整数,第2批测试数据以两个0结束;
第2批测试数据结束后,紧接着是第3批数据,每一个CASE也是两个正整数,第3批测试数据一直到数据输入结束;
输出格式
要求,每一个CASE,输出两数的最小公倍数
第1批测试数据处理完毕时,输出“group 1 done”
第2批测试数据处理完毕时,输出“group 2 done”
第3批测试数据处理完毕时,输出“group 3 done”
输入样例
2
6 10
5 12
8 16
12 18
8 4
0 0
4 5
4 6
输出样例
30
60
group 1 done
16
36
8
group 2 done
20
12
group 3 done
注意事项:
该题注意数据都用long long
代码实现:
ll GCD(ll num1, ll num2) { // 最大公约数
ll r;
while (num1 % num2) {
r = num1 % num2;
num1 = num2;
num2 = r;
}
return num2;
}
ll LCM(ll num1, ll num2) { // 最小公倍数
return ((num1 * num2) / GCD(num1, num2));
}
int main() {
IOS;
ll num1, num2;
// 有给定Case的数量
ll n; cin >> n;
while (n--) {
cin >> num1 >> num2;
cout << LCM(num1, num2) << endl;
}
cout << "group 1 done" << endl;
// 没有给定Case的数量,但是有输入结束的标志
for (;;) {
cin >> num1 >> num2;
if (num1 == 0 && num2 == 0) {
break;
}
cout << LCM(num1, num2) << endl;
}
cout << "group 2 done" << endl;
// 没有给定Case的数量,读取数据到最后一行
while (cin >> num1 >> num2) {
cout << LCM(num1, num2) << endl;
}
cout << "group 3 done" << endl;
return 0;
}
-
递归和分治 (Recursion and Divide and Conquer)
1142 巡逻的士兵
时间限制 : 1000MS 代码长度限制 : 10KB
Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。
现要求统计按这样的方法,总共可能有多少种不同的正好三个士兵去侦察的士兵组合方案。
注 : 按上法得到少于三士兵的情况不统计。
1 <= N <= 2的32次方 - 1
输入格式
有多行(可能有上百行,尽量优化代码),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,输出针对N的方案数
直到没有数字
输入样例
10
4
0
输出样例
2
0
注意事项:
该题数据量很大,需要使用备忘录 (memo) 来记录计算过的结果来减少重复计算
代码实现:
constexpr auto MAX = 1000000; // 定义限制
vector<int> memo(MAX, 0); // 备忘录
int solve(int n) {
if (n < MAX && memo[n] != 0) { // 在备忘录中的计算直接返回之前计算的结果
return memo[n];
}
if (n < 3) { // 最后剩下少于3个士兵的情况不用去巡逻
return 0;
}
if (n == 3) { // 最后剩下刚好3个士兵都要去巡逻,为一个方案
return 1;
}
int res = solve(n / 2) + solve((n + 1) / 2); // 最终的结果是去掉偶数站位的士兵的方案数加上去掉奇数站位的士兵的方案数
if (n < MAX) {
memo[n] = res;
}
return res;
}
int main() {
IOS;
int n;
while (cin >> n && n) {
cout << solve(n) << endl;
}
return 0;
}
18441 偷懒的士兵
时间限制 : 1000MS 代码长度限制 : 10KB
Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现有一个“聪明”的士兵,经常通过选择站在合适的初始位置,成功避免被选中去侦察。这引起了陈教官的注意。
陈教官希望你编写一个程序,当给定士兵数之后,输出有多少个位置上的士兵是不可能被选中去巡逻的。
注 : 按上法得到少于三士兵的情况不用去巡逻。
1 <= N <= 21亿
输入格式
有多行(可能有上百行,请尽量优化代码),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,不可能被选中去巡逻的位置数
直到没有数字
输入样例
10
6
0
输出样例
4
0
注意事项:
该题数据量很大,需要使用备忘录 (memo) 来记录计算过的结果来减少重复计算
代码实现:
#define MAX 1000000
vector<int> memo(MAX, 0);
int solve(int n) {
if (n < MAX && memo[n]) {
return memo[n];
}
if (n < 3) {
return 0;
}
if (n == 3) {
return 1;
}
int res = solve(n / 2) + solve((n + 1) / 2);
if (n < MAX) {
memo[n] = res;
}
return res;
}
int main() {
IOS;
int n;
while (cin >> n && n) {
cout << n - 3 * solve(n) << endl; // 要得到不会选去巡逻的士兵数量,即是所有士兵的数量减去 3 * 士兵去巡逻的方案数(每次巡逻都是3个人,所以乘以3)
}
return 0;
}
18442 偷懒的士兵2
时间限制 : 1000MS 代码长度限制 : 10KB
Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现有一个“聪明”的士兵,经常通过选择站在合适的初始位置,成功避免被选中去侦察。这引起了陈教官的注意。陈教官希望你编写一个程序,当给定士兵数之后,输出不可能被选中去巡逻的最少编号位置(如果不存在不可能被选中的位置,则输出0)。
注 : 按上法得到少于三士兵的情况不用去巡逻。
1 <= N <= 100000
输入格式
有多行(不多于20行),每行一个数字N,最后一行是0
输出格式
对每一行的数字N,不可能被选中去巡逻的最小位置
直到没有数字
输入样例
9
6
0
输出样例
2
0
注意事项:
无
代码实现:
int solve(int n, int _min, int step) {
if (n < 3) { // 此时不用去巡逻,得到的结果即为此时队伍的第一名士兵的编号
return _min;
}
if (n == 3) { // 此时都要去巡逻,无法得到结果,返回INT32_MAX来排除答案
return INT32_MAX;
}
int deleteOdd = solve(n / 2, _min + step, 2 * step); // 删除掉占位为奇数的士兵,此时第一名士兵的编号为之前第一名士兵的编号加上一个跨度
int deleteEven = solve((n + 1) / 2, _min, 2 * step); // 删除掉占位为偶数的士兵, 此时第一名的士兵的编号不变
return min(deleteOdd, deleteEven); // 取最小
}
int main() {
IOS;
int n;
while (cin >> n && n) {
int result = solve(n, 1, 1);
cout << (result < INT32_MAX ? result : 0) << endl;
}
return 0;
}
19144 偷懒的士兵3
时间限制 : 1000MS 代码长度限制 : 10KB
Description
有N个士兵站成一队列, 现在需要选择几个士兵派去侦察。为了选择合适的士兵, 多次进行如下操作: 如果队列超过三个士兵, 那么去除掉所有站立位置为奇数的士兵,或者是去除掉所有站立位置为偶数的士兵。直到不超过三个战士,他们将被送去侦察。现有一个“聪明”的士兵,经常通过选择站在合适的初始位置,成功避免被选中去侦察。这引起了陈教官的注意。陈教官希望你编写一个程序,当给定士兵数,以及位置编号,判断站在该位置编号的战士是否可能被抽中巡逻。
注 : 按上法得到少于三士兵的情况不用去巡逻。
1 <= N <= 100000
输入格式
有多行(不多于10000行),每行两个数字,第一个数字是士兵人数,第二个数字是位置编号,最后一行是两个0,表示结束
输出格式
对每一行输入,输出一行结果,能被抽中输出Y,不能抽中输出N
最后一行两个0不需要
输入样例
4 1
5 1
0 0
输出样例
N
Y
注意事项:
无
代码实现:
bool Check(int n, int m) {
if (n < 3) {
return false;
}
if (n == 3) {
return true;
}
if (m % 2) { // 如果站位是奇数
return Check((n + 1) / 2, (m + 1) / 2); // 去除站位是偶数的士兵
}
else {
return Check(n / 2, m / 2); // 去除站位是奇数的士兵
}
}
int main() {
IOS;
int n, m;
while (cin >> n >> m && n && m) {
cout << (Check(n, m) ? 'Y' : 'N') << endl;
}
return 0;
}
-
排序和优先队列 (Sorting and Priority Queue)
18105 银行的叫号顺序
时间限制 : 8000MS 代码长度限制 : 10KB
Description
银行的叫号过程是一个优先队列的典型应用,假设,银行有4类客户,分别用优先级1,2,3,4表示,级别越高则更优先得到服务,例如,当前有三个人排队,两个1级客户,一个3级客户,则银行叫号时,3级客户将先得到服务,即使另两个1级有客户比他先到。当多个同级的客户将获得服务时,由先到的客户先得到服务。
假设,银行只有一个服务窗口,一次只能服务一个客户,假设该窗口每5分钟服务一个客户,即叫号的时刻分别为0分钟、5分钟、10分钟、.....如果在叫号的时侯,没有客户,银行职员会去喝杯咖啡或上个洗手间,5分钟后再继续叫号。
银行给出一系列客户到来的记录,每条记录包括“客户到来的时”,“客户等级”,“客户姓名”(由一个单词构成),请输出银行服务的客户的顺序。
输入格式
第一数字是客户的数量n(n <= 100000)此后,每一行是一个客户来访信息,包括3个数字,到来的时刻(分钟整点, 最大10的8次方)、等级、姓名(最多20个字母)(已经按到来时刻排序)
输出格式
按服务的先后顺序输出客户的姓名
输入样例
4
0 1 John
3 1 Smith
3 1 Tom
4 2 Flod
输出样例
John
Flod
Smith
Tom
注意事项:
无
代码实现:
int main() {
IOS;
int arrivalTime, level; string name;
priority_queue<pair<int, string>> q; // 排队的队列
const int levelIndex = 100000; // 客户等级所占比重的参数值
int timeIndex = 99999; // 时间所占的参数值,且随着时间推移,该参数会递减
int realTime = 0; // 实时的时间
int numberOfClient; cin >> numberOfClient;
while (numberOfClient--) {
cin >> arrivalTime >> level >> name;
for (;;) {
if (arrivalTime <= realTime) {
q.push(make_pair(level * levelIndex + timeIndex, name));
timeIndex--;
break;
}
if (!q.empty()) {
cout << q.top().second << endl;
q.pop();
}
realTime += 5;
}
}
while (!q.empty()) {
cout << q.top().second << endl;
q.pop();
}
return 0;
}
18216 银行服务
时间限制 : 8000MS 代码长度限制 : 10KB
Description
银行通过叫号来决定服务用户的顺序,假设,银行有4类客户,分别用优先级1,2,3,4表示,级别越高则更优先得到服务,例如,当前有三个人排队,两个1级客户,一个3级客户,则银行叫号时,3级客户将先得到服务,即使另两个1级的客户比他先到。当多个同级的客户将获得服务时,由先到的客户先得到服务。
假设,银行只有一个服务窗口,一次只能服务一个客户,假设该窗口每5分钟服务一个客户,即叫号的时刻分别为0分钟、5分钟、10分钟、.....如果在叫号的时侯,没有客户,银行职员会去喝杯咖啡或上个洗手间,5分钟后再继续叫号。
有一种情况,银行工作到一定时间是要下班的,所以到一定时间,如果后面还有客户,将不再提供服务。
银行给出一系列客户到来的记录,每条记录包括“客户到来的时间”,“客户等级”,“客户姓名”(由一个单词构成),请输出该银行这一轮服务的客户的顺序。
输入格式
第一行两个数字,第一数字是客户的数量n(n <= 100000),第二个数字是银行关门的时间,到这个时间,即关门,该点及之后,不再叫号,但之前已经叫号的客户会继续服务到完结。
此后,每一行是一个客户来访信息,包括3个数字,到来的时刻(分钟整点, 最大10的8次方)、等级、姓名(最多20个字母)(已经按到来时刻排序,注意:同一时刻可能会同时到来多个客户,这时若为同等级的,数据出现得早的稍早得到服务)
输出格式
按服务的先后顺序输出客户的姓名
输入样例
4 12
0 1 John
3 1 Smith
3 1 Tom
4 2 Flod
输出样例
John
Flod
Smith
注意事项:
该题相比上一题只是多了一个时间的限制
代码实现:
int main() {
IOS;
priority_queue<pair<int, string>> q;
c