藍橋杯第九屆(2018年)省賽軟件類C++A組解題報告
Apare_xzc 2020/3/8
1. 分數
分析:
原式 = 1+1-(2^19) = 1+(2^19-1)/2^19 = (2^19+2^19-1)/2^19 = (2^20-1)/(2^19)
代碼:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int up = (1<<20)-1;
int down = (1<<19);
int g = __gcd(up,down);
up/=g; down/=g;
cout<<up<<"/"<<down<<endl;
return 0;
}
答案:1048575/524288
2. 星期一
分析:
可以用日曆做吧。知道今天是星期幾,幾月幾號,就可一推出2000年12月31日是星期幾,就可以算出來了。
代碼:
#include <bits/stdc++.h>
using namespace std;
int isLeap(int y) {
if(y%400==0||y%4==0&&y%100!=0)return 1;
return 0;
}
int main() {
int tot = 0;
for(int i=1901; i<=2000; ++i)
tot += 365+isLeap(i);
--tot;
int t2 = 0;
for(int i=2001; i<=2019; ++i)
t2 += 365 + isLeap(i);
t2 += 31+29+8; //今天是2020年3月8日 星期日
cout<<"t2 = "<<t2<<endl;
t2%=7;
cout<<t2<<endl; //t2 = 0
//說明2000年12月31日是星期日
printf("%d / %d = %d ... %d\n",tot,7,tot/7,tot%7);
//36524 / 7 = 5217 ... 5 說明1901/1/1是週日往回數5天,爲週二
cout<<tot/7<<endl; //5217
return 0;
}
答案:5217
3. 乘積尾零
輸入數據:
5650 4542 3554 473 946 4114 3871 9073 90 4329
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899
1486 5722 3135 1170 4014 5510 5120 729 2880 9019
2049 698 4582 4346 4427 646 9742 7340 1230 7683
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649
6701 6645 1671 5978 2704 9926 295 3125 3878 6785
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074
689 5510 8243 6114 337 4096 8199 7313 3685 211
分析:
我們只要把每個數都分解,求出因子爲2的個數和因子爲5的個數,取較小值即可。
代碼:
#include <bits/stdc++.h>
using namespace std;
int main() {
freopen("in_C.txt","r",stdin);
int x;
int cnt2 = 0, cnt5 = 0;
while(cin>>x) {
while(x%2==0) x/=2,cnt2++;
while(x%5==0) x/=5,cnt5++;
}
cout<<min(cnt2,cnt5)<<endl; //31
return 0;
}
答案:31
4. 第幾個幸運數字
計算59084709587505
是第幾個幸運數字。
分析:
我們可以處理出所有比59084709587505
小的幸運數,然後就知道它是第幾個了。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL M = 59084709587505ll;
LL p3[100]={1},p5[100]={1},p7[100]={1};
LL r[1000000];
int main()
{
int c3,c5,c7;
for(int i=1;;++i) {
p3[i] = p3[i-1]*3;
if(p3[i]>M) {
c3 = i-1;break;
}
}
for(int i=1;;++i) {
p5[i] = p5[i-1]*5;
if(p5[i]>M) {
c5 = i-1;break;
}
}
for(int i=1;;++i){
p7[i] = p7[i-1]*7;
if(p7[i]>M) {
c7 = i-1;break;
}
}
int cnt = 0;
for(int i=0;i<=c3;++i) {
for(int j=0;j<=c5;++j) {
if(p3[i]>=M/p5[j]) break;
for(int k=0;k<=c7;++k) {
if(p3[i]*p5[j]>M/p7[k]) break;
r[cnt++] = p3[i]*p5[j]*p7[k];
}
}
}
sort(r,r+cnt);
for(int i=0;i<cnt;++i)
cout<<r[i]<<" ";cout<<endl;
cout<<"cnt = "<<cnt<<endl;
return 0;
}
答案:1905
5. 打印程序
題目代碼如下:
#include <stdio.h>
#include <stdlib.h>
void show(char* buf, int w){
int i,j;
for(i=0; i<w; i++){
for(j=0; j<w; j++){
printf("%c", buf[i*w+j]==0? ' ' : 'o');
}
printf("\n");
}
}
void draw(char* buf, int w, int x, int y, int size){
if(size==1){
buf[y*w+x] = 1;
return;
}
int n = _________________________ ; //填空
draw(buf, w, x, y, n);
draw(buf, w, x-n, y ,n);
draw(buf, w, x+n, y ,n);
draw(buf, w, x, y-n ,n);
draw(buf, w, x, y+n ,n);
}
int main()
{
int N = 3;
int t = 1;
int i;
for(i=0; i<N; i++) t *= 3;
char* buf = (char*)malloc(t*t);
for(i=0; i<t*t; i++) buf[i] = 0;
draw(buf, t, t/2, t/2, t);
show(buf, t);
free(buf);
return 0;
}
答案:size/3
(從輸入的行數即可得到)
6. 航班時間
樣例輸入:
3
17:48:19 21:57:24
11:05:18 15:14:23
17:21:07 00:31:46 (+1)
23:02:41 16:13:20 (+1)
10:19:19 20:41:24
22:19:04 16:41:09 (+1)
樣例輸出:
04:09:05
12:10:39
14:22:05
分析:
我們可以計算出往返的兩次時間差,然後相加即可抵消時差,求算數平均數即爲答案。
代碼:
#include <bits/stdc++.h>
using namespace std;
char str[100];
void show(int x) {
int s,m,h;
s = x%60;
x/=60;
m = x%60;
h = x/60;
printf("%02d:%02d:%02d\n",h,m,s);
}
int getDif() {
int sh,sm,ss,eh,em,es,add=0,stime,etime,dif;
cin.getline(str,100);
sscanf(str,"%d:%d:%d %d:%d:%d (+%d)",&sh,&sm,&ss,&eh,&em,&es,&add);
eh += add*24;
stime = (sh*60+sm)*60+ss;
etime = (eh*60+em)*60+es;
dif = etime-stime;
return dif;
}
int main() {
// freopen("in_F.txt","r",stdin);
int T;
cin>>T;
int dif1,dif2,ans;
cin.getline(str,100);
while(T--) {
dif1 = getDif();
dif2 = getDif();
ans = (dif1+dif2)/2;
show(ans);
}
return 0;
}
7. 三體攻擊
樣例輸入:
2 2 2 3
1 1 1 1 1 1 1 1
1 2 1 2 1 1 1
1 1 1 2 1 2 1
1 1 1 1 1 1 2
樣例輸出:
2
分析:
分析得,暴力可以得70分。那就寫暴力吧。三維的數據結構不會寫。網上有個OI選手,寫了一篇二分答案+三維差分check的題解,可以拿100分。
代碼:
#include <bits/stdc++.h>
using namespace std;
int a[1000000+10];
int main() {
int A,B,C,m,n,la,ra,lb,rb,lc,rc,h,id;
scanf("%d%d%d%d",&A,&B,&C,&m);
n = A * B * C;
for(int i=1;i<=n;++i)
scanf("%d",a+i);
int ans = -1;
for(int ca=1;ca<=m;++ca) {
scanf("%d%d%d%d%d%d%d",&la,&ra,&lb,&rb,&lc,&rc,&h);
if(ans!=-1) continue;
for(int i=la;i<=lb&&ans==-1;++i) {
for(int j=la;j<=lb&&ans==-1;++j) {
for(int k=lc;k<=rc;++k) {
id = ((i-1)*B+j-1)*C+k;
a[id] -= h;
if(a[id]<0) {
ans = ca; break;
}
}
}
}
}
cout<<ans<<endl;
return 0;
}
8. 全球變暖
樣例輸入:
7
.......
.##....
.##....
....##.
..####.
...###.
.......
樣例輸出:
1
分析:
dfs判連通,然後dfs淹大陸,再dfs即可。見代碼
代碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
char a[N][N];
bool v[N][N];
int n;
int dx[] = {0,0,1,-1};
int dy[] = {-1,1,0,0};
bool ok(int x,int y)
{
return x>=0&&x<n&&y>=0&&y<n;
}
void dfs(int x,int y) {
if(v[x][y]) return;
v[x][y] = true;
for(int i=0; i<4; ++i) {
int s = x + dx[i], t = y + dy[i];
if(!ok(s,t)||v[s][t]||a[s][t]!=a[x][y]) continue;
dfs(s,t);
}
}
int main() {
freopen("in_H.txt","r",stdin);
cin>>n;
for(int i=0; i<n; ++i)
scanf("%s",a[i]);
int cnt = 0;
for(int i=0; i<n; ++i) {
for(int j=0; j<n; ++j) {
if(a[i][j]=='.'||v[i][j]) continue;
dfs(i,j);
++cnt;
}
}
vector<pair<int,int> > ve;
for(int i=0; i<n; ++i) {
for(int j=0; j<n; ++j) {
if(a[i][j]=='.') continue;
bool tag = true;
for(int k=0;k<4;++k)
{
int x = i+dx[k], y = j+dy[k];
if(!ok(x,y)) continue;
if(a[x][y]=='.') {
tag = false; break;
}
}
if(!tag) ve.push_back(make_pair(i,j));
}
}
int sz = ve.size();
for(int i=0;i<sz;++i)
a[ve[i].first][ve[i].second] = '.';
memset(v,false,sizeof(v));
int cnt2 = 0;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j) {
if(v[i][j]||a[i][j]=='.') continue;
dfs(i,j); ++cnt2;
}
cout<<cnt-cnt2<<endl;
return 0;
}
9. 倍數問題
分析:
由於k<=1000很小,所以我們從這裏找突破口。我們把%k餘數爲相同的數放到同一個列表裏,然後從大到小排序。k的倍數,餘數要麼都是0,要麼三個數%k的餘數之和爲k。我們可以dfs,找出三個數之和爲K的所有情況。
代碼:
#include <bits/stdc++.h>
using namespace std;
int a[100005];
vector<int> v[1000];
bool cmp(int x,int y) {
return x>y;
}
int main() {
int n,k;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; ++i)
scanf("%d",a+i),v[a[i]%k].push_back(a[i]);
for(int i=0; i<k; ++i)
if(v[i].size()) sort(v[i].begin(),v[i].end(),cmp);
int ans = -1000;
if(v[0].size()>=3) ans = v[0][1]+v[0][2]+v[0][0];
for(int i=0; i*3<=k; ++i) {
for(int j=i; i+j*2<=k; ++j) {
int t = k-i-j;
if(i==j&&j==t) {
if(v[i].size()>=3) ans = max(ans,v[i][0]+v[i][1]+v[i][2]);
} else if(i==j) {
if(v[i].size()>=2&&v[t].size()>=1) ans = max(ans,v[i][0]+v[i][1]+v[t][0]);
} else if(j==t) {
if(v[j].size()>=2&&v[i].size()>=1) ans = max(ans,v[i][0]+v[j][0]+v[j][1]);
} else {
if(v[i].size()&&v[j].size()&&v[t].size()) ans = max(ans,v[i][0]+v[j][0]+v[t][0]);
}
}
}
cout<<ans<<endl;
return 0;
}
10. 付賬問題
樣例輸入1:
5 2333
666 666 666 666 666
樣例輸出1:
0.0000
樣例輸入2:
10 30
2 1 4 7 4 8 3 6 4 7
樣例輸出2:
0.7928
分析:
方差最小,就是相互最接近,每個數都和平均數差最小。我們先求出平均數。帶的錢不足平均數的,全部拿出來。剩下的帳和剩下的人再求平均數,然後按照新的平均數出錢。這樣貪心,方差最小。
代碼:
#include <bits/stdc++.h>
using namespace std;
double a[500008];
int main() {
int n;
double tot,ever;
scanf("%d%lf",&n,&tot);
for(int i=1; i<=n; ++i)
scanf("%lf",a+i);
ever = tot/n;
sort(a+1,a+1+n);
int s = n+1;
double sum = 0;
for(int i=1; i<=n; ++i) {
if(a[i]*n>tot) {
s = i;
break;
}
sum += a[i];
}
if(s==n+1) {
puts("0.0000");
return 0;
}
ever = (tot-sum)/(n+1-s);
for(int i=s; i<=n; ++i) a[i] = ever;
ever = tot / n;
double All = 0;
for(int i=1; i<=n; ++i)
All += (ever-a[i])*(ever-a[i]);
All /= n;
All = sqrt(All);
printf("%.4f\n",All);
return 0;
}
2020.3.13
1:17
xzc