怎么说呢,就是一个信心赛,哈哈哈,打着给自己信心的,这种偶尔做做,抚慰一下菜鸡的内心,还是可以的。(但不能沉迷在简单题的基础,还是要不断错,不断学习的)
比赛:
http://oj.hzjingma.com/contest/problem?id=72
试题A:第N个素数(暴力枚举)
要求我们输入,第 300 个素数是多少(从 2 3 5 开始...),直接暴力枚举即可。把在一定范围内的所有素数找出来,然后数第 300 个是多少即可
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5;
int p[MAXN];
void init()
{
memset(p, 0, sizeof(p));
for(int i = 2;i < MAXN; ++i)
{
if(p[i] == 0)
{
for(int j = 2 * i; j < MAXN; j += i)
{
p[j] = 1;
}
}
}
}
int main()
{
init();
int cnt = 0;
for(int i = 2;i < MAXN; ++i)
{
if(p[i] == 0) ++cnt;
if(cnt == 300)
{
cout << i << endl;
break;
}
}
return 0;
}
试题B:coffee的签到题(规律题)
自己计算 4 5 6 7 8 9 的情况,发现,奇数就是 yes,偶数就是 no
这里要注意的一个地方,输入的数 n,可能是 10 ^ (100) 大小,因此无法用 long long 存储,那么只能用字符串输入。然后判断奇偶性的时候,相当于把字符串看成一个数,直接判断最后一位即可(把字符串最后一位变成 数)。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin >> s;
int val = s[s.size() - 1] - '0';
if(val % 2 == 1) cout << "yes" << endl;
else cout << "no" << endl;
return 0;
}
试题C:不一样的日期(模拟)
这道题主要就是模拟,根据输入的 d ,是 > 0 还是 < 0 分开情况模拟。、
假设经过了 d 天,我们先得到 相当于是经过了,几年,几月,几日,然后原来的年月日,加上 或者 减去。这个时候,要判断,日会不会是 <= 0 或者 > 13(这样子就相当于要从月份中,借位或者进位),同样的,月会不会 <= 0 或者 > 23 (这样子就相当于要从年份中,借位或者进位)。
总体而言,就是一道模拟题,对应处理即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 23 个月,13天
int Y, M, D, d;
cin >> Y >> M >> D >> d;
if(d > 0)
{
int y = d / (23 * 13);
d = d % (23 * 13);
int m = d / 13;
d = d % 13;
Y += y;
M += m;
D += d;
while(D > 13)
{
D -= 13;
++M;
}
while(M > 23)
{
M -= 23;
++Y;
}
}
else if(d < 0)
{
d = -d;
int y = d / (23 * 13);
d = d % (23 * 13);
int m = d / 13;
d = d % 13;
Y -= y;
M -= m;
D -= d;
while(D <= 0)
{
D += 13;
--M;
}
while(M <= 0)
{
M += 23;
--Y;
}
}
printf("%d %d %d\n", Y, M, D);
return 0;
}
试题D:三位分节法(模拟)
就是按照输入的数,三个三个之间加入逗号,而且输入的数很大,所以直接用字符串存,同时输出也用字符串存。
那么处理的时候,从前往后处理,后面的都是 3 个 3 个一起的,所以一开始,先取 3 的模,如果不为 0,说明前面多东西,比如 1000,应该结果是 1, 000。这样子先处理 取 3 的模结果的前几个数。
这里要注意,可能数是 23 那么结果应该是 23 而不能是 23,
所以在把前面几个多余的处理后,要不要加 , 就要考虑后面还有没有数
然后剩下的后面,都是 3 个 3 个 一组,先把 3 个数输出后 再加上 ,
这里要注意的就是 234, 234 也就是说,最后的 3 个输出后 是不加上 , 的 所以要注意了
因此这道题是一道模拟题,主要就是要注意一些点即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string str;
cin >> str;
int len = str.size();
string res = "";
int i = 0;
if(len % 3 != 0) // 先把前面多的,处理
{
for(;i < (len % 3); ++i)
{
res += str[i];
}
if(i != len) res += ","; // 要不要加,就是看,后面还有没有数
}
int cnt = 0;
for(;i < len; ++i)
{
++cnt;
res += str[i];
if(cnt == 3 && i != len - 1) // 三个一组后,要加上逗号,但是要求,最后一组的时候,后面是不跟逗号的
{
res += ",";
cnt = 0;
}
}
cout << res << endl;
return 0;
}
试题E:水坑题(取模运算)
简单题,根据输入的 a b 数据大小,用 long long 存,同时根据取模的运算 (a * b) % MOD = (a % MOD) * (b % MOD) % MOD。
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1e9 + 7;
int main()
{
long long a, b;
cin >> a >> b;
long long res = ((a % MOD) * (b % MOD)) % MOD;
cout << res << endl;
return 0;
}
试题F:站队(排序 + 结构体)
根据题意,一个学生有三个性质,因此想到用结构体来存储。然后要根据三个性质,对学生进行排序
典型的排序问题,直接重写 sort 函数中 cmp 即可(用结构体的三个值来进行判断)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 15;
struct Stu // 结构体
{
int H;
int D;
int number;
};
Stu stu[MAXN]; // 学生数组
bool cmp(Stu a, Stu b) // 结构体排序
{
// 三个排序规则
if(a.H != b.H) return a.H < b.H;
if(a.D != b.D) return a.D < b.D;
return a.number < b.number;
}
int main()
{
int N;
scanf("%d", &N);
for(int i = 0;i < N; ++i)
{
scanf("%d %d", &stu[i].H, &stu[i].D);
stu[i].number = i + 1;
}
sort(stu, stu + N, cmp);
for(int i = 0;i < N; ++i)
{
printf("%d", stu[i].number);
if(i == N-1)
printf("\n");
else
printf(" ");
}
return 0;
}
试题G:冷门进制(思维 + 因子2,3 的个数)
根据我们要把一个十进制的值,变为 六进制,比如对于 18 ,我们变成 六进制(类似,十进制变成二进制,不断除法取模取商)的过程是: 18 / 6 = 3 .. 0,3 / 6 = 0 ... 3,所以变成六进制是 30。
那么我们发现,要看末尾有几个 0,那就是在一开始 除 6 的时候,是整除的,也就是模为 0。
那么相当于,我们要在所有数相等中,找到因子是 6 的个数,同时,由于 6 = 2 * 3,因此,结果就是,找到所有数中的,因子 2 和 3 的个数。
同时,由于 2 和 3 要各一个,才有一个 6 存在。因此,最后的末尾 0 个数,取决于 因子 2 和 3 中的最少数(因为最少数,意味着,2和3 可以配对起来,多出来的 2 或者 3 无法配对成为 6).
因此,问题,变成了,找打,所有数中 因子 2 和 3 的个数
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main()
{
int n;
cin >> n;
LL two = 0, three = 0; // 因子 2 和 3 的个数
LL a;
for(int i = 0;i < n; ++i)
{
cin >> a;
LL tep = a;
while(tep % 2 == 0) // 这个数,不断除 2,得到 因子是 2 的个数
{
++two;
tep /= 2;
}
tep = a;
while(tep % 3 == 0) // 同理
{
++three;
tep /= 3;
}
}
// cout << two << " " << three << endl;
LL res = min(two, three);
cout << res << endl;
return 0;
}
试题H:拯救阿拉德大陆(容斥原理)
题目很长,都是废话,重点是:有 1 ~ n ,总共 n 个人,找出能被 a,b,c,d 其中一个整除的所有数的个数。
根据 n / a,其实就是,在 1 ~ n 中,能被 a 整除的个数。
容斥原理的意思是,假设有多个集合 A,B,C,要求,所有集合的并集结果 : A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C。
如果扩展到多个集合,也就是:
那么,这道题,A 区间就是 能被 a 整除的个数,B就是能被 b 整除的个数,C就是 能被 c 整除的,D就是能被 d 整除的。
结果不是单独这几个集合之和,因为会有重复部分,所以是并集关系
这里要注意 A∩B 值得是,能同时被 a 和 b 整除的个数(也就是找出 a 和 b 的最小公倍数)。其他的类似
所以这道题中,还有会求,最小公倍数(利用最大公约数 gcd 来求,最快)
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL gcd(LL a, LL b) // 最大公约数
{
if(b == 0) return a;
return gcd(b, a % b);
}
LL maxMultiple(LL a, LL b) // 最小公倍数
{
LL c = gcd(a, b);
LL res = (a * b) / c;
return res;
}
int main()
{
LL n;
cin >> n;
LL a, b, c, d;
cin >> a >> b >> c >> d;
LL res = 0;
res += n / a;
res += n / b;
res += n / c;
res += n / d;
res -= n / maxMultiple(a, b);
res -= n / maxMultiple(a, c);
res -= n / maxMultiple(a, d);
res -= n / maxMultiple(b, c);
res -= n / maxMultiple(b, d);
res -= n / maxMultiple(c, d);
res += n / maxMultiple(maxMultiple(a, b), c);
res += n / maxMultiple(maxMultiple(a, b), d);
res += n / maxMultiple(maxMultiple(a, c), d);
res += n / maxMultiple(maxMultiple(b, c), d);
res -= n / maxMultiple(maxMultiple(maxMultiple(a, b), c), d);
cout << res << endl;
return 0;
}
试题I:切香肠(二分)
对可能的长度进行二分查找,然后利用这个长度,去计算 n 个香肠的可以被切成几部分
如果已经满足 k 份,也就是长度可以更大区间里找。如果不满足,说明长度要短一点,去更小区间找。因此可以利用二分,不断的缩小区间
因此,时间复杂度是 O(N * logL),不会超时。
还有注意的一个地方,就是由于这个是有小数,小数有精度,就很麻烦。根据题目,最多精度是 0.01,因此我们可以把所有情况,都增大 100 倍,变成整数来处理,最后得到的长度,再 除100 变回小数。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4 + 15;
int L[MAXN];
int main()
{
int n, k;
cin >> n >> k;
for(int i = 0;i < n; ++i)
{
double a;
scanf("%lf", &a);
L[i] = a * 100;
}
int left = 0, right = 1e8;
int res = 0;
while(left < right)
{
int mid = (left + right) / 2;
int cnt = 0;
for(int i = 0;i < n; ++i)
{
cnt += L[i] / mid;
}
// cout << left << " " << right << endl;
// cout << mid << " " << cnt << endl << endl;
if(cnt >= k)
{
res = max(res, mid);
left = mid + 1;
}
else
right = mid;
}
// printf("%d\n", res);
printf("%0.2lf\n", res / 1.0 / 100);
return 0;
}
试题J:馋嘴羊(BFS)
根据题目,其实就是,从起点,找到可以从这个点,走到的其他点的数量,那么可以考虑用DFS 或者 BFS。但是DFS可能会超时,因为它是每一次都会有 4 种可能,题目总共有 n * m 个点,每个点可能有 4 种DFS下去的情况,那么这样子可能复杂度很大(实际上没那么大,因为一些走过的点,就不会考虑,所以有一些点的不会说有 4 种可能)。(但是当 n m 很大的时候,的确是有超时的可能性)
所以能用BFS,就用BFS。
我们将起点放入队列,然后取出点后,判断这个点可以走到哪些点,满足条件的点,才进队列。因此,最后其实就是求,总共有多少个点进过队列。
因此,对于BFS而言,每个点只考虑一次,因此时间复杂度是 O(N * M) 不会超市
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 15;
int vis[MAXN][MAXN];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
int main()
{
int n, m, x, y;
cin >> n >> m >> x >> y;
vector<string> grid;
for(int i = 0;i < n;++i) // 一开始的图
{
string tep;
cin >> tep;
grid.push_back(tep);
}
queue<pair<int, int> > q; // 队列放的 x 和 y一个左边
while(!q.empty()) q.pop();
memset(vis, 0, sizeof(vis));
int res = 0;
if(grid[x][y] == '1') // 从起点开始
{
q.push(make_pair(x, y));
vis[x][y] = 1;
++res;
}
while(!q.empty())
{
int cx = q.front().first, cy = q.front().second;
q.pop();
for(int i = 0;i < 4;++i) // 从这个点开始往四个方向走,当满足条件,才进队列
{
int tx = cx + dx[i], ty = cy + dy[i];
if(tx < 0 || tx >= n || ty < 0 || ty >= m) continue;
if(grid[tx][ty] == '0') continue;
if(vis[tx][ty] == 1) continue;
vis[tx][ty] = 1;
++res;
q.push(make_pair(tx, ty));
}
}
cout << res << endl;
return 0;
}