2015第六屆藍橋杯競賽感悟


  之前對算法一直是敬畏的,覺得很難去學習,但是通過藍橋杯競賽也算是強迫自己認真學習了一個多月的算法,發現算法也是可以學的。

  前天競賽就結束了,一直拖到今天才來寫一篇總結,其實這次競賽收貨真的蠻大的,自己以前一直不夠重視內功的培養,現在能有這么一個機會來修煉內容還是挺開心的。

  感覺這次考試題和前兩屆去比確實難度有所增加,第九題緩存沒有寫好,第十題壓根就沒來的及做...其實第十題下來想想是能做的,只是考前最短路徑這種動態規划題做的不多,所以在比賽場上就有點怯了...雖然是很想得一等獎參加決賽的,不過照這局勢看難了...

 

今年的算法題(由於答案沒有出,我的也不一定正確,以后會進行修改):

一、

獎券數目

有些人很迷信數字,比如帶“4”的數字,認為和“死”諧音,就覺得不吉利。
雖然這些說法純屬無稽之談,但有時還要迎合大眾的需求。某抽獎活動的獎券號碼是5位數(10000-99999),要求其中不要出現帶“4”的號碼,主辦單位請你計算一下,如果任何兩張獎券不重號,最多可發出獎券多少張。

請提交該數字(一個整數),不要寫任何多余的內容或說明性文字。

思路:

前幾題應該都是送分的,沒什么特別需要說的,這題可以用全排列去做,比賽的時候我用了最直接的方法,10000~99999五位數字五個for循環去判斷就行了,讓每一位數都不為4得到的總數就是答案。

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 /* run this program using the console pauser or add your own getch, system("pause") or input loop */
 5 
 6 int main(int argc, char *argv[]) {
 7     int i,j,k,l,m;
 8     int count=0;
 9     for(i = 1 ; i <= 9;i++)
10     {
11         if(i != 4)
12         for(j = 0 ; j <=9 ; j++)
13         {
14             if(j != 4)
15             {
16                 for(k = 0 ; k <= 9 ;k++)
17                 {
18                     if(k != 4)
19                     {    
20                         for(l = 0 ; l <= 9 ;l++)
21                         {
22                             if(l != 4)
23                             {
24                                 for(m = 0 ; m <= 9 ;m++)
25                                 {
26                                     if(m!=4)
27                                     {
28                                         printf("%d\n",10000*i + 1000*j + 100*k + 10*l +m);
29                                         count++;
30                                     }
31                                 }
32                             }
33                         }
34                     }
35                 }
36             }
37         }
38     }
39     printf("%d",count);
40     return 0;
41 }

 

二、

星系炸彈

在X星系的廣袤空間中漂浮着許多X星人造“炸彈”,用來作為宇宙中的路標。
每個炸彈都可以設定多少天之后爆炸。
比如:阿爾法炸彈2015年1月1日放置,定時為15天,則它在2015年1月16日爆炸。
有一個貝塔炸彈,2014年11月9日放置,定時為1000天,請你計算它爆炸的准確日期。

請填寫該日期,格式為 yyyy-mm-dd 即4位年份2位月份2位日期。比如:2015-02-19
請嚴格按照格式書寫。不能出現其它文字或符號。

思路:

求日期類的問題也是常見問題,解這種題我的思路是把要求的分為三部分,第一部分是題目給的當前年份的所剩余的天數,第二部分是兩個年份中差的年數,第三部分是目標年所超過的天數。就這一題來說,間隔是1000天,第一部分的時間是52天,第二部分是正年數所占有的天數,第三部分就是目標所占天數。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int check(int year)
 5 {
 6     if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
 7         return 1;
 8     return 0;
 9 }
10 
11 int main(int argc, char *argv[]) {
12     int year = 2014,month = 12,day = 31,dis = 1000 - 31 - (30-9),temp;
13     int months[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
14     while(dis > 0)
15     {
16         year+=1;
17         if(check(year))
18             if(dis >= 366)
19                 dis-= 366;
20             else
21             {
22                 while(dis > 0)
23                 {
24                     temp = month-1;
25                     if(temp+1 == 12)
26                         temp = 0;
27                     if(dis > months[temp])
28                     {
29                         month = temp+1;
30                         dis -= months[temp];
31                         if(temp == 1 && check(year))
32                             dis -= 1;
33                     }
34                     else
35                     {
36                         day = dis;
37                         dis = 0;
38                     }
39                 }
40             }
41         else
42             if(dis >= 365)
43                 dis-=365;
44             else
45             {
46                 while(dis > 0)
47                 {
48                     temp = month;
49                     if(temp+1 == 13)
50                         temp = 0;
51                     if(dis > months[temp])
52                     {
53                         month = temp+1;
54                         dis -= months[temp];
55                         if(temp == 1 && check(year))
56                             dis -= 1;
57                     }
58                     else
59                     {
60                         day = dis;
61                         dis = 0;
62                     }
63                 }
64             }
65     }
66     printf("%d-%d-%d",year,month,day);
67     return 0;
68 }

三、

三羊獻瑞

觀察下面的加法算式:

    祥 瑞 生 輝
+  三 羊 獻 瑞
-------------------
三 羊 生 瑞 氣

(如果有對齊問題,可以參看【圖1.jpg】)

其中,相同的漢字代表相同的數字,不同的漢字代表不同的數字。

請你填寫“三羊獻瑞”所代表的4位數字(答案唯一),不要填寫任何多余內容。

思路:

這題是一個典型的全排列問題,直接用深度優先遍歷(DFS)就可以了,給每一個數字一個編號,一共有8個不同數字,所以就是0~9的8位全排列,注意”祥“和”三“的位置不能為0就行了。

 

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<math.h>
 4 #include<string.h>
 5 
 6 void swap(int *a, int *b)
 7 {
 8     int temp;
 9     temp = *a;
10     *a = *b;
11     *b = temp;    
12 }
13 
14 void f(int array[10],int deep)
15 {
16     int i,n1,n2,n3;
17     if(deep == 8)
18     {
19         n1 = array[0] * 1000 + array[1] * 100 + array[2] * 10 + array[3];
20         n2 = array[4] * 1000 + array[5] * 100 + array[6] * 10 + array[1];
21         n3 = array[4] * 10000 + array[5] * 1000 + array[2] * 100 + array[1] * 10 + array[7];
22         if(n1 + n2 == n3)
23         {
24             printf("%d + %d = %d\n",n1,n2,n3);
25         }
26     }
27     for(i = deep ; i < 10 ;i++)
28     {
29         swap(&array[deep],&array[i]);
30         //編號后0,4位不能為0
31         if((deep == 0 || deep == 4)&&array[deep] == 0)
32         {    
33             swap(&array[deep],&array[i]);
34             continue;
35         }
36         f(array,deep+1);
37         swap(&array[deep],&array[i]);
38     }
39 }
40 
41 int main()
42 {
43     int array[10] = {1,2,3,4,5,6,7,8,9,0};
44     f(array,0);
45     return 0;
46 }

 

四、

格子中輸出

StringInGrid函數會在一個指定大小的格子中打印指定的字符串。
要求字符串在水平、垂直兩個方向上都居中。
如果字符串太長,就截斷。
如果不能恰好居中,可以稍稍偏左或者偏上一點。

下面的程序實現這個邏輯,請填寫划線部分缺少的代碼。

#include <stdio.h>
#include <string.h>

void StringInGrid(int width, int height, const char* s)
{
    int i,k;
    char buf[1000];
    strcpy(buf, s);
    if(strlen(s)>width-2) buf[width-2]=0;
    
    printf("+");
    for(i=0;i<width-2;i++) printf("-");
    printf("+\n");
    
    for(k=1; k<(height-1)/2;k++){
        printf("|");
        for(i=0;i<width-2;i++) printf(" ");
        printf("|\n");
    }
    
    printf("|");
    
    printf("%*s%s%*s",_____________________________________________);  //填空
              
    printf("|\n");
    
    for(k=(height-1)/2+1; k<height-1; k++){
        printf("|");
        for(i=0;i<width-2;i++) printf(" ");
        printf("|\n");
    }    
    
    printf("+");
    for(i=0;i<width-2;i++) printf("-");
    printf("+\n");    
}

int main()
{
    StringInGrid(20,6,"abcd1234");
    return 0;
}

對於題目中數據,應該輸出:
+------------------+
|           |
|     abcd1234     |
|           |
|           |
+------------------+

(如果出現對齊問題,參看【圖1.jpg】)

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

思路:

這一題只需要注意觀察,因為目標字符串左右各有5個空格,又因為輸出格式為%*s%s%*s,所以很明顯只需要填寫&"     ",buf,&"     "就可以了。

 

五、

 

九數組分數

 

1,2,3...9 這九個數字組成一個分數,其值恰好為1/3,如何組法?

 

下面的程序實現了該功能,請填寫划線部分缺失的代碼。

#include <stdio.h>

void test(int x[])
{
    int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
    int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
    
    if(a*3==b) printf("%d / %d\n", a, b);
}

void f(int x[], int k)
{
    int i,t;
    if(k>=9){
        test(x);
        return;
    }
    
    for(i=k; i<9; i++){
        {t=x[k]; x[k]=x[i]; x[i]=t;}
        f(x,k+1);
        _____________________________________________ // 填空處
    }
}
    
int main()
{
    int x[] = {1,2,3,4,5,6,7,8,9};
    f(x,0);    
    return 0;
}

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

思路:

又是一道典型的全排列問題,沒有什么好說的,在for循環中是試探着遞歸的,所以當回朔后要把試探結果復原,所以只需要把上面那行代碼復制下來即可

{t=x[k]; x[k]=x[i]; x[i]=t;}

 

六、

加法變乘法

我們都知道:1+2+3+ ... + 49 = 1225
現在要求你把其中兩個不相鄰的加號變成乘號,使得結果為2015

比如:
1+2+3+...+10*11+12+...+27*28+29+...+49 = 2015
就是符合要求的答案。

請你尋找另外一個可能的答案,並把位置靠前的那個乘號左邊的數字提交(對於示例,就是提交10)。

注意:需要你提交的是一個整數,不要填寫任何多余的內容。

思路:

這題直接暴力解了,先用一個循環來找第一個*位置,第二個循環來找下一個*位置,最后一個循環來計算結果就行了。

#include<stdio.h>

int main()
{
    int i,j,k,result = 0;
    //第一個乘號只能出現在前1~47個數字后面 
    for(i = 1;i<=47;i++)
    {
        //第二個乘號只能出現在i+1~48個數字后面 
        for(j = i+1 ; j<= 48;j++)
        {
            result = 0;
            for(k = 1 ; k <= 49 ;k++)
            {
                if(i == k && j == i+1)
                {
                    result = result + (k*(k+1)*(k+2));
                    k+=2;
                    continue;
                }
                if(i == k || j == k)
                {
                    result = result + (k*(k+1));
                    k++;
                    continue;        
                }
                else
                {
                    result += k;
                }
            }
            if(result == 2015)
            {
                printf("%d\n",i);
            }
        }
    }
    return 0;
}

 

七、

牌型種數

小明被劫持到X賭城,被迫與其他3人玩牌。
一副撲克牌(去掉大小王牌,共52張),均勻發給4個人,每個人13張。
這時,小明腦子里突然冒出一個問題:
如果不考慮花色,只考慮點數,也不考慮自己得到的牌的先后順序,自己手里能拿到的初始牌型組合一共有多少種呢?

請填寫該整數,不要填寫任何多余的內容或說明文字。

思路:

這題其實也沒什么說的,還是深度優先遍歷(DFS)就行了,對於每種點數的牌取法有這些可能性:不取、取一張、取兩張、取三張、取四張,我寫的可能不太好,還加上了緩存。

 

 1 #include <stdio.h>
 2 
 3 long array[13][13];
 4 
 5 long long f(int n,int deep)
 6 {
 7     long long i,count1 = 0,count2 = 0,count3 = 0,count4 = 0,count5 = 0;
 8     if(n >= 13 && deep <=13)
 9     {
10         if(n == 13)
11             return 1;
12         else
13             return 0;
14     }
15     if(deep > 13)
16         return 0;
17     if(array[n][deep])
18         return array[n][deep];
19     for(i = 1 ; i <= 13 ;i++)
20     {
21         count1=f(n,deep+1);
22         if(array[n][deep+1] == 0)
23             array[n][deep+1] = count1;
24         count2=f(n+1,deep+1);
25         if(array[n+1][deep+1] == 0)
26             array[n+1][deep+1] = count2;
27         count3=f(n+2,deep+1);
28         if(array[n+2][deep+1] == 0)
29             array[n+2][deep+1] = count3;
30         count4=f(n+3,deep+1);
31         if(array[n+3][deep+1] == 0)
32             array[n+3][deep+1] = count4;
33         count5=f(n+4,deep+1);
34         if(array[n+4][deep+1] == 0)
35             array[n+4][deep+1] = count5;
36     }
37     return count1+count2+count3+count4+count5;
38 }
39     
40 int main()
41 {
42     int n,i,j;
43     for(i = 0 ; i < 14 ;i++)
44     {
45         for(j = 0 ; j < 14 ;j++)
46         {
47             array[i][j] = 0;
48         }
49     }
50     printf("%I64d",f(0,0));
51     
52     return 0;
53 }

 

八、


移動距離

X星球居民小區的樓房全是一樣的,並且按矩陣樣式排列。其樓房的編號為1,2,3...
當排滿一行時,從下一行相鄰的樓往反方向排號。
比如:當小區排號寬度為6時,開始情形如下:

1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 .....

我們的問題是:已知了兩個樓號m和n,需要求出它們之間的最短移動距離(不能斜線方向移動)

輸入為3個整數w m n,空格分開,都在1到10000范圍內
w為排號寬度,m,n為待計算的樓號。
要求輸出一個整數,表示m n 兩樓間最短移動距離。

例如:
用戶輸入:
6 8 2
則,程序應該輸出:
4

再例如:
用戶輸入:
4 7 20
則,程序應該輸出:
5

資源約定:
峰值內存消耗 < 256M
CPU消耗 < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。

注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標准,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。

提交時,注意選擇所期望的編譯器類型。

思路:

這一題相當的簡單,也就在初始化數組內容的時候會有點繞,兩個居民居中的距離就等於,abs(x1-x2)+abs(y1-y2)。

數據規模:

這題數據規模其實也不大,我們只需要創建個10000X10000的2維數組即可。

 

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<math.h>
 4 #include<string.h>
 5 
 6 int array[10000][10000];
 7 
 8 int main()
 9 {
10     int i,j,w,m,n,max,temp = 1,locx1,locx2,locy1,locy2;
11     scanf("%d %d %d",&w,&m,&n);
12     max = m>n?m:n;
13     for(i = 0 ; i <= max/w ;i++)
14     {
15         for(j = 0 ; j < w ;j++)
16         {
17             if(i % 2== 0)
18             {
19                 array[i][j] = temp;
20                 if(temp == m || temp == n)
21                 {
22                     locx1 = i;
23                     locy1 = j;
24                 }
25                 temp++;
26             }
27             else
28             {
29                 array[i][w-j-1] = temp;
30                 if(temp == m || temp == n)
31                 {
32                     locx2 = i;
33                     locy2 = w-j-1;
34                 }
35                 temp++;
36             }
37         }
38     }
39     printf("%d",abs(locx1-locx2) + abs(locy1-locy2));
40     return 0;
41 }

 

九、

壘骰子

賭聖atm晚年迷戀上了壘骰子,就是把骰子一個壘在另一個上邊,不能歪歪扭扭,要壘成方柱體。
經過長期觀察,atm 發現了穩定骰子的奧秘:有些數字的面貼着會互相排斥!
我們先來規范一下骰子:1 的對面是 4,2 的對面是 5,3 的對面是 6。
假設有 m 組互斥現象,每組中的那兩個數字的面緊貼在一起,骰子就不能穩定的壘起來。
atm想計算一下有多少種不同的可能的壘骰子方式。
兩種壘骰子方式相同,當且僅當這兩種方式中對應高度的骰子的對應數字的朝向都相同。
由於方案數可能過多,請輸出模 10^9 + 7 的結果。

不要小看了 atm 的骰子數量哦~

「輸入格式」
第一行兩個整數 n m
n表示骰子數目
接下來 m 行,每行兩個整數 a b ,表示 a 和 b 數字不能緊貼在一起。

「輸出格式」
一行一個數,表示答案模 10^9 + 7 的結果。

「樣例輸入」
2 1
1 2

「樣例輸出」
544

「數據范圍」
對於 30% 的數據:n <= 5
對於 60% 的數據:n <= 100
對於 100% 的數據:0 < n <= 10^9, m <= 36


資源約定:
峰值內存消耗 < 256M
CPU消耗 < 2000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。

注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標准,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。

提交時,注意選擇所期望的編譯器類型。

思路:

這題稍微難一些,而且測試數據規模非常大,所以要做相應的處理,首先我們考慮題上給的兩個篩子的情況,題上給的測試數據中不能相互碰到的篩子是1,2兩個面,所以我們可以這樣想:首先第一個篩子面對我們的數字應該是有6中情況,但是篩子還可以旋轉同樣可以使該數字對着我們。所以我們應該找兩個面來確定篩子的一種情況,我們來用上面和前面兩個面來看。前面有6種選擇,確定前面后上面有4種選擇,所以一個篩子的狀態就有24種選擇,那么兩個就有24X24=576種,還需要減去1上2下或2上1下兩種情況。1為上有4種情況2為下有4種情況,所以算上2上1下一共應該有4x4x2=32種情況,結果就應該只有576-32=544種情況。

數據規模:

這一題我用暴力DFS到8個篩子好像就不行了,非常的大,所以需要保存每一步的過程。但是考試的時候也可能是太緊張,或者還是不熟練吧,這一點沒有處理好所以估計這題得不了幾分了...請高手多指點...

#include<stdio.h>
#include<math.h>
//做緩存的數組 
int array[100000000] = {0};
int way[24][2] = {
                      {1,2},{1,3},{1,5},{1,6},
                      {2,1},{2,3},{2,4},{2,6},
                      {3,1},{3,2},{3,4},{3,5},
                      {4,2},{4,2},{4,3},{4,6},
                      {5,1},{5,3},{5,4},{5,6},
                      {6,1},{6,2},{6,4},{6,5}
                 };

int checkWay(int n,int up,int m,int check[36][2])
{
    int i;
    int down;
    down = way[n][1]>3?way[n][1]-3:way[n][1]+3;
    for(i = 0 ; i < m ; i++)
    {
        if(check[i][0] == up && check[i][1] == down)
            return 0;
        if(check[i][1] == up && check[i][0] == down)
            return 0;
    }
    return 1;
}

long long f(int n,int up,int m,int check[36][2])
{
    int i;
    long long count = 0;
    if(n == 0)
    {
        return 1;
    }
    for(i = 0 ; i < 24 ;i++)
    {
        if(up == 0)
        {
            count += f(n-1,way[i][1],m,check);
        }
        else
        {
            if(checkWay(i,up,m,check))
            {
                count += f(n-1,way[i][1],m,check);
            }
            else
                continue;
        }
    }
    return count;
}

int main()
{
    int n,m;
    int check[36][2];
    int i,j;
    scanf("%d %d",&n,&m);
    for(i = 0; i < m ;i++)
    {
        scanf("%d %d",&check[i][0],&check[i][1]);
    }
    printf("%I64d",f(n,0,m,check));
    return 0;
}

 

 

十、

生命之樹

在X森林里,上帝創建了生命之樹。

他給每棵樹的每個節點(葉子也稱為一個節點)上,都標了一個整數,代表這個點的和諧值。
上帝要在這棵樹內選出一個非空節點集S,使得對於S中的任意兩個點a,b,都存在一個點列 {a, v1, v2, ..., vk, b} 使得這個點列中的每個點都是S里面的元素,且序列中相鄰兩個點間有一條邊相連。

在這個前提下,上帝要使得S中的點所對應的整數的和盡量大。
這個最大的和就是上帝給生命之樹的評分。

經過atm的努力,他已經知道了上帝給每棵樹上每個節點上的整數。但是由於 atm 不擅長計算,他不知道怎樣有效的求評分。他需要你為他寫一個程序來計算一棵樹的分數。

「輸入格式」
第一行一個整數 n 表示這棵樹有 n 個節點。
第二行 n 個整數,依次表示每個節點的評分。
接下來 n-1 行,每行 2 個整數 u, v,表示存在一條 u 到 v 的邊。由於這是一棵樹,所以是不存在環的。

「輸出格式」
輸出一行一個數,表示上帝給這棵樹的分數。

「樣例輸入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5

「樣例輸出」
8

「數據范圍」
對於 30% 的數據,n <= 10
對於 100% 的數據,0 < n <= 10^5, 每個節點的評分的絕對值不超過 10^6 。

資源約定:
峰值內存消耗 < 256M
CPU消耗 < 3000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。

注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標准,不要調用依賴於編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include <xxx>, 不能通過工程設置而省略常用頭文件。

提交時,注意選擇所期望的編譯器類型。

 

思路:

應該就是一個迪傑斯特拉算法,這題把它看成一個求連通圖最長路徑會好想的多。

數據規模:

考場上沒有做,數據規模也沒有認真分析。

這題沒做就先不貼代碼了,明天盡量把它做了然后補上。

 

總結:

這次比賽說實話還是平時關注這方面的太少了,所以遇到一些不尋常的題就會卡在那里,第九題做完其實還有1個小時,但是緩存一直想不明白哪里出了問題...所以就在哪里死扣了,第十題知道往最短路徑上面想,但是因為就前一天剛看的最短路徑是思路不是很有信心能寫出來,所以一上來就有點想放棄了...以后一定要改正這種思想啊...不過最重要的還是平時應該多加強寫基礎的學習...


免責聲明!

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



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