A 门牌制作
题目描述
计算从1到2020有多少个字符2。
思路
数据量很小,直接暴力,对每个数字按位统计2的数量即可。
代码
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int ans = 0;
for (int i = 1; i <= 2020; i++) {
int x = i;
while (x) {
if (x % 10 == 2)ans++;
x /= 10;
}
}
cout << ans << endl;
return 0;
}
答案
624
B 既约分数
题目描述
既约分数的定义:一个分数的分子和分母的最大公约数是1。
计算有多少个既约分数的分子和分母都是1到2020之间的整数。
思路
数据量很小,直接暴力,分子从1到2020,分母从1到2020,判断分子分母组合成的2020×2020个数中有多少个满足分子分母互质。
代码
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a%b);
}
int main()
{
int ans = 0;
for (int i = 1; i <= 2020; i++) {
for (int j = 1; j <= 2020; j++) {
if (gcd(i, j) == 1)ans++;
}
}
cout << ans << endl;
return 0;
}
答案
2481215
C 蛇形填数
题目描述
如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。
1 2 6 7 15 …
3 5 8 14 …
4 9 13 …
10 12 …
11 …
…
容易看出矩阵第二行第二列中的数是5。请你计算矩阵中第 20 行第 20 列的数是多少?
思路
将蛇形矩阵顺时针旋转45度得:
1
3 2
4 5 6
10 9 8 7
11 12 13 14 15
……
每行的数字数量等于当前行的行数,奇数行从左至右填充,偶数行从右至左填充。原(1,1)对应现(1,1),原(2,2)对应现(3,2),原(3,3)对应现(5,3),……,推知原(x,x)对应现(2x-1,x)。
故原第20行第20列对应现第39行第20列。
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 40;
int a[maxn][maxn];
int main()
{
int cur = 1;
for (int i = 1; i <= 40; i++) {
int cnt = 0;
if (i & 1) {
for (int j = cur + i - 1; j >= cur; j--)a[i][++cnt] = j;
}
else {
for (int j = cur; j < cur + i; j++)a[i][++cnt] = j;
}
cur += i;
}
cout << a[39][20] << endl;
return 0;
}
答案
761
D 七段码
题目描述
思路
模拟建图,注意建图时三个晶体管公共连接点,竖向的其中一个晶体管需要占据两个格子,并且采用dfs搜索时要向八个方向搜索,才可以将这三者之间的连接关系表示出来。七个晶体管选与不选共127种情况,每种情况可以压缩成一个二进制数,这个二进制数按照一定方式处理可以解码得到相应状态。采取这样的方式可以节省代码量。
代码
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1 << 7;
bool mp[6][4], vis[6][4];
int dir[8][2] = { {1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1} };
void build(int x) {
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= 5; i++) {
if (i & 1)mp[i][2] = x & 1;
else{
mp[i][1] = mp[i + 1][1] = x & 1;
x = x >> 1;
mp[i][3] = mp[i + 1][3] = x & 1;
}
x = x >> 1;
}
}
void dfs(int x, int y) {
vis[x][y] = 1;
for (int i = 0; i < 8; i++) {
int nx = x + dir[i][0];
int ny = y + dir[i][1];
if (nx >= 1 && nx <= 5 && ny >= 1 && ny <= 3) {
if (mp[nx][ny] && !vis[nx][ny])dfs(nx, ny);
}
}
}
int main()
{
int ans = 0;
for (int i = 1; i < maxn; i++) {
build(i);
int num = 0;
for (int j = 1; j <= 5; j++) {
for (int k = 1; k <= 3; k++) {
if (mp[j][k] && !vis[j][k])dfs(j, k), num++;
}
}
if (num == 1)ans++;
}
cout << ans << endl;
return 0;
}
答案
80
E 平面分割
题目描述
20 个圆和20 条直线最多能把平面分成多少个部分?
思路
①由于圆和直线具有各自的性质,因此单独考虑二者。求直线的最大分割领域是好求的。一条直线一定可以与当前存在的所有直线全部交1次。一共交n次,就可将原n+1个区域分割成2×(n+1)个区域,即区域数加n+1。因此只需考虑每次划分平面时,当前有多少条直线。
②一个圆一定可以与当前存在的所有直线和圆全部交两次,这是由于圆具有封闭曲线的特性。每交两次,就可将原n个区域分割为2×n个区域,即区域数翻倍。因此只需考虑每次划分平面时,当前已经有多少个圆和多少条直线。
推导
①1条直线,分1+1个区域
(1)1个圆交直线,分2+2×1个区域
(2)2个圆交直线,分2+2×1+2×(1+1)个区域
(3)3个圆交直线,分2+2×1+2×(1+1)+2×(1+2)个区域
……
②2条直线相交,分1+1+2个区域
(1)1个圆交直线,分4+2×2个区域
(2)2个圆交直线,分4+2×2+2×(1+2)个区域
……
③3条直线互交,分1+1+2+3个区域
……
④4条直线互交,分1+1+2+3+4个区域
……
综上,n条直线互交,分1+1+2+3+4+……+n=1+(1+n)×n/2个区域
m个圆交直线,分1+(1+n)×n/2+2×n+2×(n+1)+2×(n+2)+……+2×(n+m-1)= 1+(1+n)×n/2+2×n×m+2×(1+2+……+m-1)= 1+(1+n)×n/2+2×n×m+m×(m-1)= 1+(1+n)×n/2+2×n×m+m×(m-1)个区域,将n=m=20代入,得最多分1+21×10+2×20×20+20×19=1391个区域。
答案
1391
F 成绩分析
题目描述
一次考试,每个学生的得分都是一个0到100的整数,请计算这次考试的最高分、最低分、平均分。
思路
注意计算平均分时开float,保留两位小数,其它的模拟即可。
代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
ll n;
cin >> n;
ll x;
ll maxn = -1, minn = 1e9, sum = 0;
for (ll i = 1; i <= n; i++) {
cin >> x;
maxn = max(maxn, x), minn = min(minn, x);
sum += x;
}
cout << maxn << endl << minn << endl;
double ans = sum*1.0 / n;
ans *= 1000;
if (ll(ans) % 10 >= 5) {
ll y = 10 - (ll(ans) % 10);
ans += y;
}
double a = ans*1.0 / 1000;
printf("%.2lf\n", a);
return 0;
}
G 回文日期
题目描述
给出一个日期,求该日期之后离它最近的回文日期和ABABBABA型日期分别是什么。
思路
为了方便,由于数据量不是那么大,直接暴力遍历给出的日期+1到99991231之间的所有可能的日期,逐一判断其是否合法。注意闰年是4的倍数且不是100的倍数,或者是400的倍数。
代码
#include<iostream>
using namespace std;
bool ishuiwen(int year, int month, int day) {
if (year / 1000 != day % 10)return false;
if ((year % 1000) / 100 != day / 10)return false;
if ((year % 100) / 10 != month % 10)return false;
if (year % 10 != month / 10)return false;
return true;
}
bool isABABBABA(int year, int month, int day) {
if (year / 1000 != (year % 100) / 10)return false;
if (year / 1000 != month % 10)return false;
if (year / 1000 != day % 10)return false;
if (day / 10 != month / 10)return false;
if (day / 10 != year % 10)return false;
if (day / 10 != (year % 1000) / 100)return false;
return true;
}
int get_year(int cal) {
int st = cal / 10000000;
int nd = (cal % 10000000) / 1000000;
int rd = (cal % 1000000) / 100000;
int th = (cal % 100000) / 10000;
int year = st * 1000 + nd * 100 + rd * 10 + th;
return year;
}
int get_month(int cal) {
int st = (cal % 10000) / 1000;
int nd = (cal % 1000) / 100;
int month = st * 10 + nd;
return month;
}
int get_day(int cal) {
int st = (cal % 100) / 10;
int nd = cal % 10;
int day = st * 10 + nd;
return day;
}
bool islegal(int year, int month, int day) {
if (month >= 13 || month <= 0)return false;
if (day <= 0)return false;
if (month == 4 || month == 6 || month == 9 || month == 11) {
if (day >= 31)return false;
}
else if (month != 2) {
if (day >= 32)return false;
}
else {
if (year % 4 == 0 && year % 100 || year % 400 == 0) {
if (day >= 30)return false;
}
else {
if (day >= 29)return false;
}
}
return true;
}
int main()
{
int cal;
cin >> cal;
int st = 0, nd = 0;
for (int cur = cal + 1; cur <= 99991231; cur++) {
int year = get_year(cur);
int month = get_month(cur);
int day = get_day(cur);
if (!islegal(year, month, day))continue;
if (ishuiwen(year, month, day) && !st)st = cur;
if (isABABBABA(year, month, day) && !nd)nd = cur;
if (st&&nd)break;
}
cout << st << endl << nd << endl;
return 0;
}
H 子串分值
题目描述
给一个字符串S,定义S的分值f(S)为S中恰好出现一次的字符个数。设串长为n,计算对于S的所有非空子串S[i……j](0<=i<=j<=n),f(S[i……j])的和是多少。
思路
要只遍历一遍就可以求出答案,那么突破口在于只有26种字符,完全可以对每个字符求一遍贡献。子串可以分为n类,每一类分别以第i号元素为结尾。
设所求字符为a,若第i号元素及之前没有出现a,则对于此类子串,a的贡献为0;若第i号元素及之前a出现过一次,则对于此类子串,a的贡献等于a出现的位置;若第i号元素及之前a出现过两次及以上,则对于此类子串,a的贡献等于离第i号元素最近两次a出现的位置之差。因此我们需要记录在第i号元素之前最近两次各元素出现的位置。最后将各种字符的贡献相加即得结果。
代码
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
cin >> s;
int ans = 0;
for (int i = 0; i < 26; i++) {
char x = 'a' + i;
int last = -1, cur = -1;
for (int j = 0; j < s.size(); j++) {
if (s[j] == x) {
if (cur == -1 && last == -1)cur = j + 1;
else if (cur != -1 && last == -1)last = cur, cur = j + 1;
else if (cur != -1 && last != -1)last = cur, cur = j + 1;
}
if (cur != -1 && last != -1)ans += (cur - last);
else if (cur != -1 && last == -1)ans += cur;
}
}
cout << ans << endl;
return 0;
}
I、J两题
难度太高,没有研究明白,这里就不给出题解了。