试题A:门牌制作
答案:624
#include <iostream>
using namespace std;
int main(){
int ans = 0;
for(int i=2; i<=2020; i++){
int t = i;
while(t){
if(t%10 == 2)
ans++;
t /= 10;
}
}
cout << ans<< endl;
return 0;
}
试题B:既约分数
答案:2481215
#include <iostream>
#include <algorithm>
using namespace std;
int gcd(int n,int m){
if(m==0)
return n;
if(n<m)
return gcd(n,m%n);
return gcd(m,n%m);
}
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;
}
试题C:蛇形填数
答案:761
思想:仔细观察发现蛇形走向一共有4种走位,分别是右、左下、下、右上。依次对4种走向进行dfs。当x=0,y=偶数时,下一步向右走;当y=0,x=奇数时,下一步向下走;当上一个位置是向右走的(该位置记op=1)或者向左下走的(该位置op=2)都记下一个位置op=2,意思是下一步要向左下走;右上同理,只不过下一个位置op=3。依次dfs直至到达x=19,y=19截止(这里横纵坐标都从0开始)。
#include <iostream>
#include <algorithm>
using namespace std;
int q[25][25],cnt=1;
void dfs(int x,int y,int op){
q[x][y]=cnt++;
if(x==19&&y==19)
cout<<q[19][19]<<endl;
if(x==20&&y==20)
return ;
if(x==0&&y%2==0){//向右
op=1;
dfs(x,y+1,op);
}else if(y==0&&x%2==1){//向下
op=3;
dfs(x+1,y,op);
}
if(op==1||op==2){//左下
op=2;
dfs(x+1,y-1,op);
}else if(op==3){//右上
dfs(x-1,y+1,op);
}
}
int main()
{
dfs(0,0,1);
return 0;
}
试题D:跑步锻炼
答案:8879
思想:先判断该年是否为闰年,再记录每个月的最多天数,依次判断该天是否为周一或初一。
#include <iostream>
#include <algorithm>
using namespace std;
int cnt,tip=6,ans;
int d[2][13]= {0,31,28,31,30,31,30,31,31,30,31,30,31,
0,31,29,31,30,31,30,31,31,30,31,30,31
};
bool is_leap_year(int num)
{
int year=num;
if(year%400==0||(year%4==0&&year%100!=0))
return true;
return false;
}
int count_day(int num,int tip)
{
for(int num=2000;num<=2020;num++){
int year=num;
int month=1;
int day=1;
int s_month=12;
if(num==2020){
s_month=10;
}
for(int j=month; j<=s_month; j++)
{
int maxday=d[0][j];
if(is_leap_year(num))
{
maxday=d[1][j];
}
for(int z=day; z<=maxday; z++,tip++)
{
cnt++;
if(tip>7)
tip=1;
if(z==1||tip==1)
cnt++;
if(num==2020&&j==10&&z==1)
return cnt;
}
}
}
}
int main()
{
cout<<count_day(2020,6)<<endl;
return 0;
}
试题F:成绩统计
思想:注意输出,是%0.0lf,不是%0.01f哦。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int cnt,tip=6,ans;
int main()
{
double n,num,c1=0,c2=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>num;
if(num>=60)
c1++;
if(num>=85)
c2++;
}
printf("%.0lf%%\n%.0lf%%\n",100*c1/n,100*c2/n);
return 0;
}
试题G:回文日期
思想:无脑暴力必然爆表。看到题目中范围是8位数,我们可以直接从年份入手,依次判断月和日是否合法,以及是否满足条件即可。其中judge函数判断是第一个回文日期,judge1函数判断的是第一个ABABBABA日期。这个代码只拿了90分,还有一个测试点没过,目前还没找出问题。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int flag,flag1;
int d[2][13]= {0,31,28,31,30,31,30,31,31,30,31,30,31,
0,31,29,31,30,31,30,31,31,30,31,30,31
};
bool is_leap_year(int num)
{
if(num%400==0||(num%4==0&&num%100!=0))
return true;
return false;
}
bool judge(int num)
{
int r_month=num/100;
int r_day=num%100;
int month=(num%10)*10+(num/10)%10;
int day=((num/100)%10)*10+num/1000;
if(month<=12&&month>0)
{
int maxday=d[0][month];
if(is_leap_year(num))
maxday=d[1][month];
if(day<=maxday){
flag=1;
printf("%d%02d%02d\n",num,month,day);
return true;
}
}
return false;
}
bool judge1(int num)
{
int r_month=num/100;
int r_day=num%100;
int month=(num%10)*10+(num/10)%10;
int day=((num/100)%10)*10+num/1000;
if(r_month!=r_day)
return false;
if(month<=12&&month>=0)
{
int maxday=d[0][month];
if(is_leap_year(num))
maxday=d[1][month];
if(day<=maxday){
flag1=1;
printf("%d%02d%02d\n",num,month,day);//注意输出要符合日期规范,20200202对,202022错。
return true;
}
}
return false;
}
int main()
{
int num,year;
cin>>num;
year=num/10000;
for(int i=year+1;i<=9999;i++){
if(!flag){
judge(i);
}
else
break;
}
for(int i=year+1;i<=9999;i++){
if(!flag1){
judge1(i);
}
else
break;
}
return 0;
}
试题H:子串分值和
思想:学长说是dp的思想,如果明白是dp就很简单,如果不知道可能就很难搞。如果按照样例那种排列组合找规则可能有点困难,我们可以换一种排列组合,更容易解决这个问题。
这样是不是就更容易发现规律啦,再附一个样例
其实就是相同的字母中最后一个总会覆盖前面所有的字母,但计算到每个字母时都要记录当下最新的一个字母。而且我们也发现,每个字符贡献的次数就是下标值+1(下标从0开始)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
char d[100005];
int p[27];
long long cnt;
int main()
{
cin>>d;
int len=strlen(d);
for(int i=0;i<len;i++){
p[d[i]-'a']=i+1;
for(int j=0;j<26;j++){
cnt+=p[j];
}
}
cout<<cnt<<endl;
return 0;
}
试题I:平面切分
思想:这题在学长讲完之后我以为我懂了(其实就是我以为而已),自己上手敲代码的时候发现计算最终值并不是直接地等于所有交点数+有效直线数(不含重合直线)+1,而是每一条直线一条一条插进去模拟计算,插入这条直线时计算该条直线与前面所有直线的交点个数,直到最后一条直线插入结束(之所以一条一条模拟而不是上来就找全部的交点是因为这中间会有重复的交点,直接计算会漏掉许多有贡献的交点,而一条一条插入不会漏掉并且会很容易发现规律(每增加一个结点就会贡献一个分割平面))。用flag1[x][y]记录当下交点,避免重复。注意flag1是用map存的,x,y均为浮点型。
这样举例是不是就容易找到规律啦,自己可以动手多画画,也可以按照我之前错误的想法一次性把所有的直线都画出来再计算交点,这时候就会发现出现问题了,因为有的点重复后并没有计算它之前的贡献值。两种思想一对比就能加深自己的模拟思想了,所以有时候你以为懂了其实是没懂哈哈。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
long ans;
map<int, map<int, int> > flag;
int q[1007][3];
int main()
{
int n,x,y,in=0;
cin>>n;
for(int i=0;i<n;i++){
scanf("%d %d", &x, &y);
if(!flag[x][y]){
flag[x][y]=1;
q[in][0]=x;
q[in][1]=y;
in++;
}
}
map<double, map<double,double> > flag1;
for(int in1=0;in1<in;in1++){
flag1.clear();
for(int in2=0;in2<in1;in2++){
if(q[in1][0]==q[in2][0])
continue;
double x=(q[in2][1]-q[in1][1])/(double)(q[in1][0]-q[in2][0]);
double y=q[in1][0]*x+q[in1][1];
if(!flag1[x][y]){
flag1[x][y]=1;
ans++;
}
}
}
cout << 1 + in + ans;
return 0;
}
E和J没写题解是因为不在我能力范围之内,所以就不补辣(嘿嘿偷懒),题解中有不对的地方请大家多多指正,谢谢呀