1. 程式人生 > >計蒜客2018藍橋杯省賽B組模擬賽(一)題目及解析(未完待續)

計蒜客2018藍橋杯省賽B組模擬賽(一)題目及解析(未完待續)

一、題目列表

A. 結果填空:年齡         分值: 3 B. 結果填空:開關燈     分值: 7 C. 結果填空:U型數字  分值: 9 D. 程式碼填空:LIS         分值: 11 E. 程式碼填空:全排列   分值: 13 F. 結果填空:數獨        分值: 16 G. 數列求值                   分值: 15 H. 封印之門                   分值: 20 I. 天上的星星                 分值: 25 J. 青出於藍勝於藍        分值: 31

二、題解

1. (3')年齡

        今天蒜頭君帶著花椰妹和朋友們一起聚會,當朋友們問起年齡的時候,蒜頭君打了一個啞謎(畢竟年齡是女孩子的隱私)說:“我的年齡是花椰妹年齡個位數和十位數之和的二倍”。
        花椰妹看大家一臉懵逼,就知道大家也不知道蒜頭君的年齡,便連忙補充道:“我的年齡是蒜頭君個位數和十位數之和的三倍”。
        請你計算:蒜頭君和花椰妹年齡一共有多少種可能情況?
        提醒:兩位的年齡都是在 [10,100) 這個區間內。

【分析】模擬+列舉

        由題意,兩個人的年齡都是十位數。因此可以使用age1表示蒜頭君的年齡,age2表示花椰妹的年齡;同時使用answer1和answer2分別表示蒜頭君和花椰妹的"啞謎",然後列舉即可。

【答案】1

#include <stdio.h>
int cnt=0;     //可能情況種數 
int main()
{
	int age1,age2; 
	int answer1,answer2;
	for(age1=10;age1<100;age1++)
	{
		for(age2=10;age2<100;age2++)
		{
			answer1=(age2/10+age2%10)*2;
			answer2=(age1/10+age1%10)*3;
			if(answer1==age1 && answer2==age2)
			{
				printf("蒜頭君-%d 花椰妹-%d\n",answer1,answer2);
				cnt++;
			}
		}
	}
	printf("%d\n",cnt);
	return 0;
}
2. (7')開關燈

        蒜頭君今天回到了老家的大宅院,老家的燈還是那中拉線的燈(拉一次為亮,再拉一次就滅),蒜頭君覺得無聊。把 1000 盞燈 3的倍數拉了一次,5的倍數拉了一次,7的倍數拉了一次(燈的編號從 1-1000,燈的初始狀態都是亮的)。這個時候蒜頭君在想還剩下幾盞燈還在亮著?

        提示:請不要輸出多餘的符號。

【分析】模擬+列舉

        此題為經典"開關問題"。列舉,需要注意對燈的編號判斷是否是3/5/7的倍數的時候需要分別進行(例:15,既是3的倍數又是5的倍數,但這盞燈需要拉兩次,也就是狀態不變)。

【答案】571

#include <stdio.h>
#define maxn 1005
int light[maxn];    //狀態陣列light記錄燈的亮滅情況 1-亮 -1-滅 
int ans=0;
int main()
{
	int i,num;
	for(i=0;i<1000;i++)   //初始狀態:所有燈亮 
		light[i]=1;
	for(i=0;i<1000;i++)   //根據編號是否是3/5/7的倍數確定是否拉燈 
	{
		num=i+1;
		if(num%3==0)
			light[i]=-light[i];
		if(num%5==0)
			light[i]=-light[i];
		if(num%7==0)
			light[i]=-light[i];
	}
	for(i=0;i<1000;i++)
	{
		if(light[i]==1)
		{
			printf("%d ",i+1);
			ans++;
		}
	}
	printf("ans=%d\n",ans);
	return 0;
}
3. (9')U型數字

        最近蒜頭君喜歡上了U型數字,所謂U型數字,就是這個數字的每一位先嚴格單調遞減,後嚴格單調遞增。比如 212 就是一個U型數字,但是 333, 98, 567, 31313,就不是U型數字。
        現在蒜頭君問你,[1,100000]有多少U型數字?
        提示:請不要輸出多餘的符號。

【分析】模擬+列舉+數位分離+標記位的靈活使用

        通過U型數字定義可知,將某個數的所有數位分離出來並按高位到低位的順序排列,一定有一位將分離出的數位劃分為兩部分,其左側部分都比該位大,且越往左越大;其右側部分也都比該位大,且越往右越大。因此此題的解題關鍵在於找到第一個下降點和第一個上升點(嚴格單調遞減與嚴格單調遞增的"分界點”),步驟如下:

        (1)列舉[1, 100000]內的所有數,需要注意的是,1~100一定不是U型數字(1~9是一位數,沒有數位上升或下降可言;10~99是兩位數,或者嚴格單調遞減,或者嚴格單調遞增,或者兩位數字相等;100找不到上升的兩個數位),故列舉的數範圍為 i∈[101, 100000];

        (2)用臨時變數temp儲存數i,進行數位分離,結果儲存在digit陣列中,同時i的位數用len儲存。然後從高位到低位分析:

        ①如果出現相鄰兩個數位相等,則不滿足嚴格單調遞減與嚴格單調遞增的要求,則可判斷該數不是U型數字;

        ②找第一個下降點和第一個上升點。若找到第一個上升點後發現有相鄰兩位是嚴格單調遞減的,或者這個數剛開始不是嚴格單調遞減的,則可判斷該數不是U型數字;

        ③若能找到第一個下降點和第一個上升點,並且這個數沒有出現②中不符合要求的情況,則這個數是U型數字。

【答案】8193

#include <stdio.h>
#define maxn 10
int ans=0;         //U型數字總數 
int digit[maxn];   //儲存數的各數位 
int main()
{
	int i,j;
	int len;       //len-列舉的數i的位數 
	int temp,curdigit;   //temp-臨時儲存列舉的數i  curdigit-當前數位 
	int firstdec,firstinc; //firstdec-第一個下降位 firstinc-第一個上升位(上升與下降的分界) 
	int suc;       //suc記錄是否出現不符合要求的情況 1-符合要求 0-不符合要求 
	//列舉過程:i<=100->不是U型數字 
	for(i=101;i<=100000;i++)  
	{
		temp=i;
		len=0;
		firstdec=firstinc=0;
		suc=1;
		while(temp!=0)
		{
			digit[len++]=temp%10;
			temp/=10;
		}
		for(j=len-1;j>0;j--)
		{
			curdigit=digit[j];
			if(curdigit==digit[j-1])
			{
				suc=0;
				break;
			}
			else
			{
				if(curdigit>digit[j-1])
				{
					if(firstdec==0)
						firstdec=1;
					if(firstinc==1)
					{
						suc=0;
						break;
					}
				}
				else
				{
					if(firstinc==0)
						firstinc=1;
					if(firstdec==0)
					{
						suc=0;
						break;
					}
				}
			}
		}
		if(suc==1 && firstdec==1 && firstinc==1)
		{
			printf("%d\n",i);
			ans++;
		}
	}
	printf("ans=%d\n",ans);
	return 0;
}

4. (11')LIS

        LIS是最長上升子序列。什麼是最長上升子序列? 就是給你一個序列,請你在其中求出一段最長嚴格上升的部分,它不一定要連續。
        就像這樣:2, 3, 4, 7 和 2, 3, 4, 6 就是序列 2 5 3 4 1 7 6 的兩個上升子序列,最長的長度是 4。

【分析】DP-最長上升子序列

【答案】f[i]=max(f[i],f[j]+1);

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

int f[10000], b[10000];
int max(int a, int b) {
    return a > b ? a : b;
}
int lis(int n) {
    memset(f, 0, sizeof f);
    int res = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < i; ++j) {
            if (b[j] < b[i]) {
                f[i]=max(f[i],f[j]+1);    //blank
            }
        }
        res = max(res, f[i]);
    }
    return res+1;
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", b + i);
    }
    printf("%d\n", lis(n));
    return 0;
}
5. (13')全排列

        相信大家都知道什麼是全排列,但是今天的全排列比你想象中的難一點。我們要找的是全排列中,排列結果互不相同的個數。

        比如:aab 的全排列就只有三種,那就是aab,baa,aba。

        程式碼框中的程式碼是一種實現,請分析並填寫缺失的程式碼。

【分析】含有重複元素的全排列問題

        關鍵:去重

【答案】vis[j] && str[i]==str[j]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 1000

char str[N], buf[N];
int vis[N], total, len;

void arrange(int num) {
    int i, j;
    if (num == len) {
        printf("%s\n", buf);
        total++;
        return;
    }
	for (i = 0; i < len; ++i) {
        if (!vis[i]) {
            for (j = i + 1; j < len; ++j) {
                if (vis[j] && str[i]==str[j]) {    //blank 
                    break;
                }
            }
            if (j == len) {
                vis[i] = 1;
                buf[num] = str[i];
                arrange(num + 1);
                vis[i] = 0;
            }
        }
    }
}
int main() {
    while (~scanf("%s", str)) {
        len = strlen(str);
        int i, j;
        for (i = 0; i < len; ++i) {
            for (j = i + 1; j < len; ++j) {
                if (str[i] > str[j]) {
                    char tmp = str[i];
                    str[i] = str[j];
                    str[j] = tmp;
                }
            }
        }
        total = 0;
        buf[len] = '\0';
        arrange(0);    //從第1個字元開始搜尋 
        printf("Total %d\n", total);
    }
    return 0;
}

6. (16')數獨

       蒜頭君今天突然開始懷念童年了,想回憶回憶童年。他記得自己小時候,有一個很火的遊戲叫做數獨。便開始來了一局緊張而又刺激的高階數獨。蒜頭君做完發現沒有正解,不知道對不對? 不知道聰明的你能否給出一個標準答案?
       標準數獨是由一個給與了提示數字的 9×9 網格組成(如下圖),我們只需將其空格填上數字,使得每一行,每一列以及每一個 3×3 宮都沒有重複的數字出現。

       輸出這個數獨得正解,輸出格式如下:

* 2 6 * * * * * *
* * * 5 * 2 * * 4
* * * 1 * * * * 7
* 3 * * 2 * 1 8 *
* * * 3 * 9 * * *
* 5 4 * 1 * * 7 *
5 * * * * 1 * * *
6 * * 9 * 7 * * *
* * * * * * 7 5 *

把上面的 * 替換成 1 - 9 就可以了
提醒:兩個數字之間要有一個空格,其他地方不要輸出多餘的符號。
本題答案不唯一,符合要求的答案均正確。

【分析】dfs+剪枝

7. (15')數列求值

對於一個含有 n+2個元素的數列A0, A1, ... , An,滿足這樣的遞迴公式Ai= (Ai1+Ai+1) /2Ci​   1in

現在我們知道A0, An+1C1, C2, ... , Cn

現在請你幫忙計算A1的值。

輸入格式

  第一行輸入一個整數n(1n1000)

  第二行輸入兩個數A0, An+1,接著是 n個數據分別是C1, C2, ... , Cn。所有的資料均是兩位小數的浮點數。

輸出格式

  輸出A1的值,結果保留兩位小數。

樣例輸入1

1

50.50 25.50

10.15

樣例輸出1

27.85

樣例輸入2

2

-756.89 52.52

172.22 67.17

樣例輸出2

-761.49

【分析】遞迴->遞推


#include <stdio.h>
#define maxlen 1010
int n;
double A0,An1;
double C[maxlen];
double X[maxlen];
double result;
int main()
{
	int i;
	scanf("%d",&n);
	scanf("%lf %lf",&A0,&An1);
	X[0]=A0;
	X[1]=0;
	for(i=1;i<=n;i++)
	{
		scanf("%lf",&C[i]);
		X[i+1]=2.0*(X[i]+C[i])-X[i-1];
	}
	result=(An1-X[n+1])/(n+1);
	printf("%.2lf\n",result);
	return 0;
}
8. (20')封印之門

  蒜頭君被暗黑軍團包圍在一座島上,所有通往近衛軍團的路都有暗黑軍團把手。幸運的是,小島上有一扇上古之神打造的封印之門,可以通往近衛軍團,傳聞至今沒有人能解除封印。

  封印之門上有一串文字,只包含小寫字母,有 k 種操作規則,每個規則可以把一個字元變換成另外一個字元。經過任意多次操作以後,最後如果能把封印之門上的文字變換成解開封印之門的文字,封印之門將會開啟。

  蒜頭君戰鬥力超強,但是不擅計算,請你幫忙蒜頭君計算至少需要操作多少次才能解開封印之門。

輸入格式

  輸入第一行一個字串,長度不大於1000,只包含小寫字母,表示封印之門上的文字。

  輸入第二行一個字串,只包含小寫字母,保證長度和第一個字串相等,表示能解開封印之門的文字。

  輸入第三行一個整數k(0k676)

  接下來k行,每行輸出兩個空格隔開的字元a,b,表示一次操作能把字元a變換成字元b

輸出格式

  如果蒜頭君能開啟封印之門,輸出最少的操作次數。否則輸出一行1

樣例輸入

abcd

dddd

3

a b

b c

c d

樣例輸出

6


9. (25')天上的星星

  在一個星光璀璨的夜晚,蒜頭君一顆一顆的數這天上的星星。

  蒜頭君給在天上巧妙的畫了一個直角座標系,讓所有的星星都分佈在第一象。天上有n顆星星,他能知道每一顆星星的座標和亮度。

  現在,蒜頭君問自己q次,每次他問自己每個矩形區域的星星的亮度和是多少(包含邊界上的星星)。

輸入格式

  第一行輸入一個整數n(1n50000)表示星星的數量。

  接下n行,每行輸入三個整數x,y,w(0x,y,w2000),表示在座標(x,y)有一顆亮度為w的星星。注意一個點可能有多個星星。

  接下來一行輸入一個整數q(1q50000),表示查詢的次數。

  接下來q行,每行輸入四個整數x1,y1,x2,y2,其中(x1,y1)表示查詢的矩形的左下角的座標,(x2,y2)表示查詢的矩形的右上角的座標,0x1x220000y1y22000

輸出格式

  對於每一次查詢,輸出一行一個整數,表示查詢的矩形區域內的星星的亮度總和。

樣例輸入

5

5 0 6

7 9 7

8 6 13

9 7 1

3 0 19

4

0 8 7 9

0 0 7 10

2 7 10 9

5 4 7 5

樣例輸出

7

32

8

0

10. (31')青出於藍勝於藍

       武當派一共有n 人,門派內 n 人按照武功高低進行排名,武功最高的人排名第 1,次高的人排名第 2...武功最低的人排名第 n。現在我們用武功的排名來給每個人標號,除了祖師爺,每個人都有一個師父,每個人可能有多個徒弟。

       我們知道,武當派人才輩出,連祖師爺的武功都只能排行到 p。也就是說徒弟的武功是可能超過師父的,所謂的青出於藍勝於藍。

       請你幫忙計算每個人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超過了他自己。

輸入格式

       輸入第一行兩個整數 n,p(1n100000,1pn)

       接下來 n1 行,每行輸入兩個整數u,v(1u,vn),表示 u 和 v 之間存在師徒關係。

輸出格式

       輸出一行 n 個整數,第 i 個整數表示武功排行為 i 的人的子弟有多少人超過了他。行末不要輸出多餘的空格

樣例輸入

10 5

5 3

5 8

3 4

3 1

2 1

6 7

8 7

9 8

8 10

樣例輸出

0 0 2 0 4 0 1 2 0 0