計蒜客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
2. (7')開關燈#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; }
蒜頭君今天回到了老家的大宅院,老家的燈還是那中拉線的燈(拉一次為亮,再拉一次就滅),蒜頭君覺得無聊。把 1000 盞燈 3的倍數拉了一次,5的倍數拉了一次,7的倍數拉了一次(燈的編號從 1-1000,燈的初始狀態都是亮的)。這個時候蒜頭君在想還剩下幾盞燈還在亮著?
提示:請不要輸出多餘的符號。
【分析】模擬+列舉
此題為經典"開關問題"。列舉,需要注意對燈的編號判斷是否是3/5/7的倍數的時候需要分別進行(例:15,既是3的倍數又是5的倍數,但這盞燈需要拉兩次,也就是狀態不變)。
【答案】571
3. (9')U型數字#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; }
最近蒜頭君喜歡上了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= (Ai−1+Ai+1) /2−Ci 1≤i≤n
現在我們知道A0, An+1和C1, C2, ... , Cn
現在請你幫忙計算A1的值。
輸入格式
第一行輸入一個整數n(1≤n≤1000)。
第二行輸入兩個數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(0≤k≤676)。
接下來k行,每行輸出兩個空格隔開的字元a,b,表示一次操作能把字元a變換成字元b。
輸出格式
如果蒜頭君能開啟封印之門,輸出最少的操作次數。否則輸出一行−1。
樣例輸入
abcd
dddd
3
a b
b c
c d
樣例輸出
6
9. (25')天上的星星
在一個星光璀璨的夜晚,蒜頭君一顆一顆的數這天上的星星。
蒜頭君給在天上巧妙的畫了一個直角座標系,讓所有的星星都分佈在第一象。天上有n顆星星,他能知道每一顆星星的座標和亮度。
現在,蒜頭君問自己q次,每次他問自己每個矩形區域的星星的亮度和是多少(包含邊界上的星星)。
輸入格式
第一行輸入一個整數n(1≤n≤50000),表示星星的數量。
接下來n行,每行輸入三個整數x,y,w(0≤x,y,w≤2000),表示在座標(x,y)有一顆亮度為w的星星。注意一個點可能有多個星星。
接下來一行輸入一個整數q(1≤q≤50000),表示查詢的次數。
接下來q行,每行輸入四個整數x1,y1,x2,y2,其中(x1,y1)表示查詢的矩形的左下角的座標,(x2,y2)表示查詢的矩形的右上角的座標,0≤x1≤x2≤2000,0≤y1≤y2≤2000。
輸出格式
對於每一次查詢,輸出一行一個整數,表示查詢的矩形區域內的星星的亮度總和。
樣例輸入
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(1≤n≤100000,1≤p≤n)。
接下來 n−1 行,每行輸入兩個整數u,v(1≤u,v≤n),表示 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