1. 程式人生 > >2016年藍橋杯省賽C/C++ A組題解(含題目)

2016年藍橋杯省賽C/C++ A組題解(含題目)

1. 網友年齡

某君新認識一網友。 當問及年齡時,他的網友說: “我的年齡是個2位數,我比兒子大27歲, 如果把我的年齡的兩位數字交換位置,剛好就是我兒子的年齡”
請你計算:網友的年齡一共有多少種可能情況?
提示:30歲就是其中一種可能哦. 請填寫表示可能情況的種數。
注意:你提交的應該是一個整數,不要填寫任何多餘的內容或說明性文字。

小學奧數或者列舉一下:7

2. 生日蠟燭

某君從某年開始每年都舉辦一次生日party,並且每次都要吹熄與年齡相同根數的蠟燭。 現在算起來,他一共吹熄了236根蠟燭。
請問,他從多少歲開始過生日party的?
請填寫他開始過生日party的年齡數。
注意:你提交的應該是一個整數,不要填寫任何多餘的內容或說明性文字。

等差數列,列舉首項:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    for (int i=1;i<=100;i++) {
        int sum=0,j=i;
        while (sum<236) {
            sum+=j++;
        }
        if (sum==236) {
            printf("%d %d\n",i,j);
        }
    }
    return 0;
}

我覺得列舉首項從1到100是比較合理的。。。有人說答案是236我覺得他可能沒救了...


正確答案:26

3. 方格填數

如下的10個格子
方格
填入0~9的數字。要求:連續的兩個數字不能相鄰。 (左右、上下、對角都算相鄰)
一共有多少種可能的填數方案?
請填寫表示方案數目的整數。
注意:你提交的應該是一個整數,不要填寫任何多餘的內容或說明性文字。

dfs,不過不優化的話有10!種,邊填邊判斷即可。

#include <bits/stdc++.h>
using namespace std;

/*本來要判斷八個格子,
 *但是由於是從左往右從上往下填的,
 *只要判斷左、左上、上、右上
*/
const int dx[]={0,-1
,-1,-1}; const int dy[]={-1,-1,0,1}; const int INF=1e9; bool used[10]; int ans=0; int a[5][5]; bool alright(int n,int x,int y) { for (int i=0;i<4;i++) { int xx=x+dx[i],yy=y+dy[i]; if (xx<1||yy<1||xx>3||yy>4) continue; if (abs(n-a[xx][yy])==1) return false; } return true; } void dfs(int x,int y) { if (x==3&&y==4) { ans++; return; } for (int i=0;i<=9;i++) { if (!used[i]&&alright(i,x,y)) { a[x][y]=i; used[i]=true; if (y==4) dfs(x+1,1); else dfs(x,y+1); used[i]=false; a[x][y]=-INF; } } } int main() { for (int i=1;i<=3;i++) { for (int j=1;j<=4;j++) { a[i][j]=-INF; } } dfs(1,2); printf("%d\n",ans); return 0; }

正確答案:1580

4. 快速排序

排序在各種場合經常被用到。 快速排序是十分常用的高效率的演算法。
其思想是:先選一個“標尺”, 用它把整個佇列過一遍篩子,
以保證:其左邊的元素都不大於它,其右邊的元素都不小於它。
這樣,排序問題就被分割為兩個子區間。 再分別對子區間排序就可以了。
下面的程式碼是一種實現,請分析並填寫劃線部分缺少的程式碼。

#include <stdio.h>

void swap(int a[], int i, int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int partition(int a[], int p, int r)
{
    int i = p;
    int j = r + 1;
    int x = a[p];
    while(1){
        while(i<r && a[++i]<x);
        while(a[--j]>x);
        if(i>=j) break;
        swap(a,i,j);
    }
    ______________________;
    return j;
}

void quicksort(int a[], int p, int r)
{
    if(p<r){
        int q = partition(a,p,r);
        quicksort(a,p,q-1);
        quicksort(a,q+1,r);
    }
}

int main()
{
    int i;
    int a[] = {5,13,6,24,2,8,19,27,6,12,1,17};
    int N = 12;

    quicksort(a, 0, N-1);

    for(i=0; i<N; i++) printf("%d ", a[i]);
    printf("\n");
    return 0;
}

注意:只填寫缺少的內容,不要書寫任何題面已有程式碼或說明性文字。

答案:swap(a,p,j) 快速排序資料

5. 消除尾一

下面的程式碼把一個整數的二進位制表示的最右邊的連續的1全部變成0
如果最後一位是0,則原數字保持不變。
如果採用程式碼中的測試資料,應該輸出:

00000000000000000000000001100111   00000000000000000000000001100000
00000000000000000000000000001100   00000000000000000000000000001100  

請仔細閱讀程式,填寫劃線部分缺少的程式碼。

#include <stdio.h>

void f(int x) 
{  
    int i; 
    for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);  
    printf("   ");

    x = _______________________;   

    for(i=0; i<32; i++) printf("%d", (x>>(31-i))&1);  
    printf("\n");  
}

int main() 
{ 
    f(103);  
    f(12);  
    return 0; 
}

注意:只填寫缺少的內容,不要書寫任何題面已有程式碼或說明性文字。

要做這道題首先要知道位運算,位運算資料
最好還要知道負數怎麼參與位運算,補碼資料
如果想要了解我的做法還要學習lowbit,lowbit資料

這題當時我想了很久,因為它必須在一行內就算出來,所以我想到了lowbit,它可以很方便的得到。
然而怎麼讓它套用上lowbit也不是一件容易的事。。。不過最後還是做出來了~

要消除x末尾所有的1,可以先把x加上1:

00000000000000000000000001100111 + 1 =
00000000000000000000000001101000

再減去新的數的lowbit:

00000000000000000000000001101000 - 00000000000000000000000000001000 =
00000000000000000000000001100000 

所以我的答案是:x+1-((x+1)&(-x-1))
當然標準答案更加簡單:x&(x+1)

6. 寒假作業

現在小學的數學題目也不是那麼好玩的。
看看這個寒假作業:
□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □
每個方塊代表1~13中的某一個數字,但不能重複。
比如:
6 + 7 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
以及:
7 + 6 = 13
9 - 8 = 1
3 * 4 = 12
10 / 2 = 5
就算兩種解法。(加法,乘法交換律後算不同的方案)
你一共找到了多少種方案?
請填寫表示方案數目的整數。
注意:你提交的應該是一個整數,不要填寫任何多餘的內容或說明性文字。

dfs一個一個填,每個等式判斷一下。

#include <bits/stdc++.h>
using namespace std;

bool used[15];
int a[15];
int ans=0;

void dfs(int dep)
{
    if (dep==13) {
        //必須整除,變成乘法判斷
        if (a[10]==a[11]*a[12]) ans++;
        return;
    }
    if (dep==10) {
        if (a[7]*a[8]!=a[9]) return;
    }
    if (dep==7) {
        if (a[4]-a[5]!=a[6]) return;
    }
    if (dep==4) {
        if (a[1]+a[2]!=a[3]) return;
    }
    for (int i=1;i<=13;i++) {
        if (!used[i]) {
            used[i]=true;
            a[dep]=i;
            dfs(dep+1);
            a[dep]=-1;
            used[i]=false;
        }
    }
}

int main()
{
    dfs(1);
    printf("%d\n",ans);
    return 0;
}

答案:64 詳見PPT。

7. 剪郵票

如【圖1.jpg】, 有12張連在一起的12生肖的郵票。
現在你要從中剪下5張來,要求必須是連著的。 (僅僅連線一個角不算相連)
比如,【圖2.jpg】,【圖3.jpg】中,粉紅色所示部分就是合格的剪取。

圖1
圖2
圖3

原諒我好像又講錯了。。。看來去年掛的就是這題。。。
這題不能直接按方向dfs。。。因為按方向的dfs實際上只是一筆畫。。。
不過我們可以用dfs選出5個格子然後算一個連通塊的大小看是不是等於5。

#include <bits/stdc++.h>
using namespace std;

int va[6][6],cor[13][2],q[6];
int ans=0;

int getsum(int x,int y)
{
    //值為0的不用計算
    if (va[x][y]==0) return 0;
    //算過一次就要清零,避免重複
    va[x][y]=0;
    //超出範圍也沒事,因為va陣列的周圍都是0
    return 1+getsum(x-1,y)+getsum(x+1,y)+getsum(x,y-1)+getsum(x,y+1);
}

void dfs(int dep,int last) {
    if (dep==5) {
        memset(va,0,sizeof(va));
        for (int i=0;i<5;i++) {
            va[cor[q[i]][0]][cor[q[i]][1]]=1;
        }
        if (getsum(cor[last][0],cor[last][1])==5) ans++;
        return;
    }
    for (int i=last+1;i<=12;i++) {
        //q陣列儲存我們選中的格子
        q[dep]=i;
        dfs(dep+1,i);
        q[dep]=-1;
    }
}

int main()
{
    //算出第n個格子的橫縱座標
    for (int i=1,n=1;i<=3;i++) {
        for (int j=1;j<=4;j++,n++) {
            cor[n][0]=i;cor[n][1]=j;
        }
    }
    dfs(0,0);
    printf("%d\n",ans);
    return 0;
}

正確答案:116

8. 四平方和

四平方和定理,又稱為拉格朗日定理:
每個正整數都可以表示為至多4個正整數的平方和。
如果把0包括進去,就正好可以表示為4個數的平方和。

比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符號表示乘方的意思)

對於一個給定的正整數,可能存在多種平方和的表示法。
要求你對4個數排序: 0 <= a <= b <= c <= d
並對所有的可能表示法按 a,b,c,d 為聯合主鍵升序排列,最後輸出第一個表示法

程式輸入為一個正整數N (N<5000000)
要求輸出4個非負整數,按從小到大排序,中間用空格分開

例如,輸入:
5
則程式應該輸出:
0 0 1 2
再例如,輸入:
12
則程式應該輸出:
0 2 2 2
再例如,輸入:
773535
則程式應該輸出:
1 1 267 838

資源約定:
峰值記憶體消耗 < 256M
CPU消耗 < 3000ms

請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入…” 的多餘內容。
所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include , 不能通過工程設定而省略常用標頭檔案。
提交時,注意選擇所期望的編譯器型別。

時間3s,直接列舉。

#include <bits/stdc++.h>
using namespace std;

void resolve(int n)
{
    int n1=n;
    for (int i=0;i<=sqrt(n1);i++) {
        int n2=n1-i*i;
        for (int j=0;j<=sqrt(n2);j++) {
            int n3=n2-j*j;
            for (int k=0;k<=sqrt(n3);k++) {
                int n4=n3-k*k;
                int l=sqrt(n4);
                if (l*l==n4) {
                    printf("%d %d %d %d\n",i,j,k,l);
                    return;
                }
            }
        }
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    resolve(n);
    return 0;
}

9. 密碼脫落

X星球的考古學家發現了一批古代留下來的密碼。
這些密碼是由A、B、C、D 四種植物的種子串成的序列。
仔細分析發現,這些密碼串當初應該是前後對稱的(也就是我們說的映象串)。
由於年代久遠,其中許多種子脫落了,因而可能會失去映象的特徵。

你的任務是: 給定一個現在看到的密碼串,計算一下從當初的狀態,它要至少脫落多少個種子,才可能會變成現在的樣子。

輸入一行,表示現在看到的密碼串(長度不大於1000)
要求輸出一個正整數,表示至少脫落了多少個種子。

例如,輸入:
ABCBA
則程式應該輸出:
0
再例如,輸入:
ABECDCBABC
則程式應該輸出:
3

資源約定:
峰值記憶體消耗 < 256M
CPU消耗 < 1000ms

請嚴格按要求輸出,不要畫蛇添足地列印類似:“請您輸入…” 的多餘內容。
所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。 注意: 所有依賴的函式必須明確地在原始檔中 #include , 不能通過工程設定而省略常用標頭檔案。
提交時,注意選擇所期望的編譯器型別。

經典水題,不過可能要先了解一下動態規劃 動態規劃資料
教材原題 最小回文代價資料 看前兩頁就可以了,教材的具體實現都是複雜化的。
原串和逆序串做LCS LCS資料

#include <bits/stdc++.h>
using namespace std;

char s[1010];
int dp[1010][1010];

int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=n;j++) {
            if (s[i]==s[n+1-j]) {
                dp[i][j]=dp[i-1][j-1]+1;
            } else {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    printf("%d\n",n-dp[n][n]);
    return 0;
}

10. 最大比例

以上各題的解法和答案,如有錯誤,請及時指出,謝謝!