第十一屆藍橋杯省賽C++A組-部分題解


A 門牌號

題意:統計 \([1,2020]\) 所有整數中,數碼 \(2\) 出現的次數

分析:直接暴力枚舉即可:

inline int f(int n){
      int ans=0;
      while(n) ans+=(n%10==2),n/=10;
      return ans;
}
...
int ans=0;
for(int i=1;i<=2020;i++) ans+=f(i);

輸出為 \(624\)


B 既約分數

題意:對於滿足 \(\displaystyle a,b\in Z,{a\over b}\) 為最簡分數的分數稱為既約分數,求 \(1\leq a,b\leq 2020\) 的既約分數個數

分析:暴力枚舉 \(a,b\) ,用 gcd 判是否互質

int ans=0;
for(int a=1;a<=2020;a++)
      for(int b=1;b<=2020;b++)
            ans+=(__gcd(i,j)==1);

輸出為 \(2481215\)


C 蛇形矩陣

題意:給出如下矩陣,求第 \(20\)\(20\)

1 2 6 7 ...
3 5 8 ...
4 9 ...
10 ...
...

分析:考慮令 \(f_n\) 為第 \(n\)\(n\) 列的值

不難看出,\(f_n\) 轉移至 \(f_{n+1}\) 需要自己所在斜線走一半+下一條斜線走完+\(f_{n+1}\) 所在斜線走一半

根據各斜線的長度,不難列出轉移方程 \(f_{n+1}=f_n+n-1+2n+n+1=f_n+4n\) ,可解出 \(\displaystyle f_n=4\sum_{i=1}^{n-1}i+f_1=2n(n-1)+1\)

代入 \(n=20\) 得到 \(f_{20}=761\)


D 七段碼

題意:求多少種不同的七段碼表示方法,各個亮起的燈管互相連接,且至少有一個燈管亮起

分析:用數組儲存燈管間的連接情況,枚舉 \(2^7\) 種情況,用 bfs 檢查是否滿足,統計答案即可

#include<bits/stdc++.h>
using namespace std;
bool connect[7][7]={
    {0,1,0,0,0,1,0},
    {1,0,1,0,0,0,1},
    {0,1,0,1,0,0,1},
    {0,0,1,0,1,0,0},
    {0,0,0,1,0,1,1},
    {1,0,0,0,1,0,1},
    {0,1,1,0,1,1,0}
};
inline int isconnect(int S){
    int que[16],Head=0,Tail=0,NS=0;
    bool vis[16]={0};
    for(int i=0;i<7;i++) if( (S>>i)&1 ){
        que[++Tail]=i;
        vis[i]=1;
        break;
    }
    while(Head<Tail){
        int now=que[++Head];
        NS|=1<<now;
        for(int i=0;i<7;i++)
            if( ((S>>i)&1)&&connect[now][i]&&!vis[i] ){
                que[++Tail]=i;
                vis[i]=1;
            }
    }
    return NS==S;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int Ans=0;
    for(int i=1;!(i>>7);i++) Ans+=isconnect(i);
    cout<<Ans;
    cout.flush();
    return 0;
}

輸出為 \(80\)


E 直線與圓

題意:用 \(20\) 條直線與 \(20\) 個圓,求最多能將平面分為幾個部分

分析:任意線(包括直線與曲線)之間,不交於同一點,則能將平面分為盡可能多的部分

根據歐拉定理 \(V-E+F-T=1,T=0\) ,求解出 \(F=1+E-V\) ,只需求解出點數 \(V\) 與邊數 \(V\) 即可求解

根據我們的分析,任意兩直線即可有一交點,個數為 \(\displaystyle (^{20}_2)\) ;任意兩圓之間有兩個交點,個數為 \(\displaystyle 2\cdot (^{20}_2)\) ;任意圓與直線之間有兩個交點,個數為 \(20\times 20\times 2\)

\(\displaystyle V=(^{20}_2)+2\cdot (^{20}_2)+20\times 20\times 2=1370\)

同樣根據我們的分析,一條直線被剩余的 \(19\) 條直線各交於一點,被 \(20\) 個圓各交於兩點,故線上有 \(19+20\times 2=59\) 個點,一條直線被分為 \(60\) 條邊;一個圓被剩余的 \(19\) 個圓、 \(20\) 條直線各交於兩點,共 \((19+20)\times 2=78\) 個點,一個圓被分為 \(78\) 條邊

\(\displaystyle E=60\times 20+78\times 20=2760\)

因此得到答案: \(\displaystyle F=1+2760-1370=1391\)


F 成績分析

題意:給出 \(n\) 個學生的成績,求最高分、最低分與平均值

分析:直接在輸入的時候儲存最大值、最小值和分數和,最后分數和除去人數得到平均分

#include<iostream>
#include<iomanip>
using namespace std;
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int N,S,MinS,MaxS,SumS;
    cin>>N>>S;
    MinS=MaxS=SumS=S;
    for(int i=2;i<=N;i++)
        cin>>S,MinS=min(MinS,S),MaxS=max(MaxS,S),SumS+=S;
    cout<<MaxS<<'\n'<<MinS<<'\n'<<fixed<<setprecision(2)<<SumS*1.0/N;
    cout.flush();
    return 0;
}

G 回文年份

題意:輸入一個日期,輸出嚴格后於日期的第一個 ABCDDCBA 型日期,和第一個 ABABBABA 型日期。保證有解。

分析:第一種日期,直接枚舉對應月份的對應日期,進行翻轉,儲存好 \(366\) 個日期后排序,查找第一個比輸入大的即可(數據量太小,懶得寫二分了)。第二種日期,由於月份和日期相同,直接枚舉月份,進行翻轉,儲存好 \(12\) 個日期后和第一個儲存方法相同。

#include<iostream>
#include<algorithm>
using namespace std;
int Day1[512],Day2[512];
inline int built(int month,int day){
    return ((day%10*10+day/10)*100+(month%10*10+month/10))*10000+month*100+day;
}
inline void pre(){
    int cntday[]={0,31,29,31,30,31,30,31,31,30,31,30,31},CntDay1=0;
    for(int month=1;month<=12;month++)
        for(int day=1;day<=cntday[month];day++)
            Day1[++CntDay1]=built(month,day);
    sort(Day1+1,Day1+367);
    for(int month=1;month<=12;month++)
        Day2[month]=built(month,month);
    sort(Day2+1,Day2+13);
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    pre();
    int N;
    cin>>N;
    for(int i=1;i<=366;i++) if(Day1[i]>N) {
        cout<<Day1[i]<<'\n';
        break;
    }
    for(int i=1;i<=12;i++) if(Day2[i]>N) {
        cout<<Day2[i]<<'\n';
        break;
    }
    cout.flush();
    return 0;
}

H 子串序列

題意:求一個字符串 \(S\) 的所有子串中,每個子串出現僅一次的字符個數和

分析:對於每一個字符單獨考慮:對於第 \(i\) 個字符 \(c\) ,設其前面第一個與之相同的字符出現的位置為 \(pre_i\) (沒有則為 \(0\)),后面第一個為 \(nxt_i\) (沒有則為 \(n+1\)

則對於滿足 \(l\in(pre_i,i],r\in [i,nxt_i)\) 的所有區間 \([l,r]\) ,僅出現一個字符 \(c\) ,故對每個區間都產生 \(1\) 的貢獻,總貢獻即為 \((i-pre_i)\cdot (nxt_i-i)\) ,每個統計起來即可

#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
string s;
int Pre[MAXN],Nxt[MAXN],PreIndex[26];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    getline(cin,s);
    for(int i=0;i<s.size();i++){
        if(PreIndex[s[i]-'a']) Nxt[ PreIndex[s[i]-'a'] ]=i+1;
        Pre[i+1]=PreIndex[s[i]-'a'];
        Nxt[i+1]=s.size()+1;
        PreIndex[s[i]-'a']=i+1;
    }
    ll Ans=0;
    for(int i=1;i<=s.size();i++) Ans+=1ll*(i-Pre[i])*(Nxt[i]-i);
    cout<<Ans;
    cout.flush();
    return 0;
}

I 荒島探測

題意:給定一個三角形荒島,在荒島上的某個人距離兩給定點的距離之和不得超過給定值 \(L\) ,問其可覆蓋的面積大小

分析:(口胡算法,現場未實現)

動點到兩頂點距離之和不超過某定值,圖形為一橢圓。問題等價於給定橢圓與給定三角形的交集圖形大小。

在橢圓上逆時針順序取 \(10^6\) 個點,按序連接,構成 \(10^6\) 個向量。將這些向量與三角形的三個向量求解半平面交,若有交集,則輸出大小;否則輸出 \(0\)


J 字串排序

題意:求解一字符串,滿足條件:按照冒泡排序算法,需要交換的次數為給定次數 \(V\) ;若有多解,輸出最短的;若仍有多傑,輸出字典序最小的

分析:待補


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM