A-门牌制作
【答案】
624
【参考程序】
#include <stdio.h>
int main()
{
int cnt=0;
for(int i=1;i<=2020;i++)
{
int j=i;
while(j)
{
if(j%10==2)
cnt++;
j/=10;
}
}
printf("%d",cnt);
return 0;
}
B-既约分数
【答案】
2481215
【解题思路】
gcd函数用于计算最大公约数,效率较高。当时用的暴力法,也能跑出结果。
【参考程序1】
#include <stdio.h>
int gcd(int a,int b)
{
if(b==0)
return a;
else
return gcd(b,a%b);
}
int main()
{
int cnt=0;
for(int i=1;i<=2020;i++)
for(int j=1;j<=2020;j++)
if(gcd(i,j)==1) cnt++;
printf("%d",cnt);
return 0;
}
【参考程序2】
#include <stdio.h>
bool IsPrim(int a,int b)
{
for(int i=2;i<a;i++)
if(a%i==0&&b%i==0)
return false;
return true;
}
int main()
{
int cnt=0;
for(int i=1;i<=2020;i++)
for(int j=1;j<i;j++)
if(IsPrim(i,j))
cnt+=2; // a/b 与 b/a 算两种
printf("%d",cnt+1); // 1/1 作单独考虑
return 0;
}
C-蛇形填数
【答案】
761
【解题思路】
找规律,主对角线元素依次递增4的倍数,可以再写几个数来验证。
【参考程序】
#include <stdio.h>
int main()
{
int ret=1;
for(int i=1;i<20;i++)
{
ret+=i*4;
}
printf("%d",ret);
return 0;
}
D-跑步锻炼
【答案】
8879
【注意事项】
注意闰年的判断即可。
【参考程序】
#include <stdio.h>
int Month[12]={31,29,31,30,31,30,31,31,30,31,30,31};
int main()
{
int year=2000;
int month=1;
int day=1;
int week=6;
int cnt=0;
while(year<2020||month<10||day==1)
{
if(day==1||week==1)
cnt+=2;
else
cnt++;
if(day==Month[month-1])
{
day=1;
if(month==12)
{
month=1;
year++;
if((year%4==0&&year%100!=0)||(year%400==0))
Month[1]=29;
else Month[1]=28;
}else
month++;
}else
day++;
week=(week+1)%7;
}
printf("%d",cnt);
return 0;
}
E-七段码
【答案】
80
【解题思路】
采用dfs+并查集的方法,dfs用于枚举所有可能的情况,并查集判断是否连成一片。
对于并查集不了解的可以查看 算法学习笔记(1):并查集
【参考程序】
#include <stdio.h>
#define MAXN 10
//a-1 b-2 c-3 d-4 e-5 f-6 g-7
int fa[MAXN];
int e[MAXN][MAXN]={};
int ans=0;
int v[MAXN]={};
void init()
{
for(int i=0;i<MAXN;i++)
fa[i]=i;
}
int find(int x)
{
return fa[x]==x?x:(fa[x]=find(fa[x]));
}
void merge(int i,int j)
{
fa[find(i)]=find(j);
}
void dfs(int n)
{
init();
if(n>7)
{
for(int i=1;i<=7;i++)
for(int j=1;j<i;j++)
if(e[i][j]==1&&v[i]==1&&v[j]==1)
{
int fx=find(i),fy=find(j);
if(fx!=fy) fa[fx]=fy; //merge(i,j);
}
int k=0;
for(int i=1;i<=7;i++)
if(v[i]&&fa[i]==i) k++;
if(k==1) ans++;
return;
}
v[n]=1;
dfs(n+1);
v[n]=0;
dfs(n+1);
}
int main()
{
//刻画连通关系
e[1][2]=e[1][6]=1;
e[2][1]=e[2][3]=e[2][7]=1;
e[3][2]=e[3][4]=e[3][7]=1;
e[4][3]=e[4][5]=1;
e[5][4]=e[5][6]=e[5][7]=1;
e[6][5]=e[6][7]=e[6][1]=1;
e[7][2]=e[7][3]=e[7][5]=e[7][6]=1;
dfs(1);
printf("%d",ans);
return 0;
}
F-成绩统计
提交程序请点击 这里
【样例输入】
7
80
92
56
74
88
100
0
【样例输出】
71%
43%
【参考程序】
#include <stdio.h>
int main()
{
int n;
int grade;
int pass=0;
int good=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&grade);
if(grade>=85)
good++;
if(grade>=60)
pass++;
}
printf("%.0f%%\n%.0f%%",double(pass)/n*100,double(good)/n*100);
return 0;
}
G-回文日期
提交程序请点击 这里
【样例输入】
20200202
【样例输出】
20211202
21211212
【解题思路】
按照年份遍历,然后判断构成的日期是否合法、是否满足题目条件。
【参考程序】
#include <stdio.h>
int Month[12]={31,28,31,30,31,30,31,31,30,31,30,31};
int getTxt(int year)
{
int ret=year;
int month=year%10;
year/=10;
month=month*10+year%10;
year/=10;
int day=year%10;
year/=10;
day=day*10+year%10;
ret=ret*100+month;
ret=ret*100+day;
return ret;
}
bool IsLegal(int year)
{
int month=year%10;
year/=10;
month=month*10+year%10;
year/=10;
int day=year%10;
year/=10;
day=day*10+year%10;
if(month<1||month>12)
return false;
int Mday=Month[month-1];
if(month==2&&((year%4==0&&year%100!=0)||(year%400==0)))
Mday++;
if(day>Mday)
return false;
else
return true;
}
bool IsBest(int txt)
{
int num[8]={};
for(int i=0;i<8;i++)
{
num[i]=txt%10;
txt/=10;
}
if(num[0]==num[2]&&num[0]==num[5]&&num[0]==num[7]&&num[1]==num[3]&&num[1]==num[4]&&num[1]==num[6])
return true;
else return false;
}
int main()
{
int n;
scanf("%d",&n);
int year=n/10000;
int txt=getTxt(year);
while(!IsLegal(year)||txt<=n)
{
year++;
txt=getTxt(year);
}
printf("%d\n",txt);
while(!IsLegal(year)||!IsBest(txt))
{
year++;
txt=getTxt(year);
}
printf("%d",txt);
return 0;
}
H-子串分值和
提交程序请点击 这里
【样例输入】
ababc
【样例输出】
28
【解题思路】
法一:需要遍历所有子串,时间复杂度最起码也要O(n2),大概能跑掉6/10个测试点。方法是相同起始位置的子串,记录下前n个字符构成的f值,如果新增加的字母从未出现过,也就是计数值为1,sum加一,代表当前子串的f值。也就是说,每个子串的f值由前一个子串的f值加上新增加的元素的影响来计算。
法二:计算每个字符的贡献度,如果字符各不相同,第i个字符的贡献度为i*(n-i+1),如果有相同的元素出现,要扣除掉已有元素的影响,公式为(i-i上次出现的位置)*(n-i+1)。公式看不懂的戳 这里
【参考程序1】
#include <iostream>
using namespace std;
int main()
{
long long cnt=0;
long long sum=0;
string s;
cin>>s;
for(int i=0;i<s.length();i++)
{
int character[26]={};
sum=0;
for(int j=i;j<s.length();j++)
{
character[s[j]-'a']++;
if(character[s[j]-'a']==1) sum++;
cnt+=sum;
}
}
cout<<cnt;
return 0;
}
【参考程序2】
#include <iostream>
using namespace std;
int main()
{
long long cnt=0;
int last[26]={};
string s;
cin>>s;
int n=s.length();
for(int i=1;i<=n;i++)
{
int j=s[i-1]-'a';
cnt+=1ll*(i-last[j])*(n-i+1);
last[j]=i;
}
cout<<cnt;
return 0;
}
I-平面切分
提交程序请点击 这里
【样例输入】
3
1 1
2 2
3 3
【样例输出】
6
【解题思路】
N=0时,平面数为1(用例中不会出现)
N=1时,平面数为2
N=2时,若平行平面数为3,若相交平面数为4
N=3时,至少会新增加一个平面,每与其他直线产生一个交点,产生平面数加一
基于上述想法,对于每条直线,计算与其他直线的交点,然后存入集合中,集合的大小即为不同交点的个数。
【注意事项】
要去除重复直线的影响,当直线平行时无需计算交点。
【参考程序】
#include <iostream>
#include <set>
using namespace std;
long double s[1001][2];
bool st[1001]={};
pair<long double,long double> p;
int main()
{
int N;
long long cnt=0;
cin>>N;
for(int i=1;i<=N;i++)
{
cin>>s[i][0]>>s[i][1];
set<pair<long double,long double> > points;
for(int j=1;j<i;j++)
{
if(st[j]) continue;
if(s[i][0]==s[j][0])
{
if(s[i][1]==s[j][1])
{
st[i]=true;
break;
}else continue;
}
p.first=(s[j][1]-s[i][1])/(s[i][0]-s[j][0]);
p.second=s[i][0]*p.first+s[i][1];
points.insert(p);
}
if(!st[i]) cnt+=points.size()+1;
}
cout<<cnt+1;
return 0;
}
【写在最后的话】
双非本科大学生,自认为对算法感兴趣,要求自己写题解,一方面是对自身掌握情况作一个总结,也希望能帮助到大家。参考了非常多的博客、题解,在此也就不一一列举了。
最后一题还没吃透,有时间再更新~