2020藍橋杯省賽B組部分題解

2020藍橋杯省賽B組部分題解

A題 門牌製做

【問題描述】

小藍要爲一條街的住戶製做門牌號。
這條街一共有 2020 位住戶,門牌號從 1 到 2020 編號。
小藍製做門牌的方法是先製做 0 到 9 這幾個數字字符,最後根據須要將字
符粘貼到門牌上,例如門牌 1017 須要依次粘貼字符 一、 0、 一、 7,即須要 1 個
字符 0, 2 個字符 1, 1 個字符 7。
請問要製做全部的 1 到 2020 號門牌,總共須要多少個字符 2 ?




ios

【解題思路】

一道簡單題。模擬1~2020,用四個字母記錄每位上的數字,若是是2,sum++便可。算法

【代碼實現】

#include <iostream>
using namespace std;
int main(){ 
    int g,s,b,q,sum=0;
    for (int i=1; i<=2020; i++) { 
        g=i%10;
        s=(i/10)%10;
        b=(i/100)%10;
        q=(i/1000)%10;
        if (g==2) { 
            sum++;
        }
        if (s==2) { 
            sum++;
        }
        if (b==2) { 
            sum++;
        }
        if (q==2) { 
            sum++;
        }
    }
    cout<<sum<<endl;
}

答案:624。

B題 既約分數

【問題描述】

若是一個分數的分子和分母的最大公約數是1,這個分數稱爲既約分數。例如,3/4 , 5/2 , 1/8 , 7/1都是既約分數。請問,有多少個既約分數,分子和分母都是1 到2020 之間的整數(包括1和2020)?函數

【解題思路】

寫一個子函數計算兩個數的公約數,若是找到大於1的公約數,則return false。主函數用兩個for循環遍歷全部分子分母的可能(注意:分子能夠大於分母學習

【代碼實現】

#include<iostream>
using namespace std;
int minn(int x,int y){ 
	if(x>y){ 
		return y;
	}else{ 
		return x;
	}
}
bool gongyueshu(int x,int y){ 
	if(x==1||y==1){ 
		return true;
	}
	for(int i=2;i<=minn(x,y);i++){ 
		if(x%i==0&&y%i==0){ 
			return false;
		}
	}
	return true;
}
int main(){ 
	int i,j;
	int num=0;
	for(i=1;i<=2020;i++){ 
		for(j=1;j<=2020;j++){ 
			if(gongyueshu(i,j)){ 
				num++;
			}
		}
	}
	cout<<num<<endl;
}

答案:2481215。

C題 蛇形填數

【問題描述】

以下圖所示,小明用從1 開始的正整數「蛇形」填充無限大的矩陣。
在這裏插入圖片描述
容易看出矩陣第二行第二列中的數是5。請你計算矩陣中第20 行第20 列的數是多少?

優化

【解題思路】

能夠寫代碼模擬過程。更簡單的方法是利用填數規則找規律,對於第n行n列的數x,x必定在第2n-1斜列(第1斜列有一個數爲1,第2斜列有兩個數爲二、3,… ,第n斜列有n個數),
那麼先計算前2n-2斜列一共有多少數(等差數列求和),
再加上x所在斜列前面有多少數(觀察可知,數字5在第3斜列的第2個,數字13在第5斜列的第3個,… ,數字x在第2n-1斜列的第n個)。

spa

答案:761。

D題 跑步鍛鍊

【問題描述】

小藍天天都鍛鍊身體。
正常狀況下,小藍天天跑 1 公里。若是某天是週一或者月初(1 日),爲了
激勵本身,小藍要跑 2 公里。若是同時是週一或月初,小藍也是跑 2 公里。
小藍跑步已經堅持了很長時間,從 2000 年 1 月 1 日週六(含)到 2020 年
10 月 1 日週四(含)。請問這段時間小藍總共跑步多少公里?



設計

【解題思路】

顯然能夠用代碼從2000-01-01模擬至2020-10-01,須要注意閏年的判斷。
但對於這種日期題,最好的方法是excel,以下圖。
在這裏插入圖片描述
利用excel的日期相減功能,計算出期間有多少天,從而計算出有多少個星期一。
在這裏插入圖片描述
再利用excel篩選功能,找出有多少個1號的週一,從而便能計算出結果。




excel

答案:8879。

E題 七段碼

【問題描述】

小藍要用七段碼數碼管來表示一種特殊的文字。
在這裏插入圖片描述
code

七段碼上圖給出了七段碼數碼管的一個圖示,數碼管中一共有7 段能夠發光的二極管,分別標記爲a, b, c, d, e, f, g。小藍要選擇一部分二極管(至少要有一個)發光來表達字符。在設計字符的表達時,要求全部發光的二極管是連成一片的。
例如:b 發光,其餘二極管不發光能夠用來表達一種字符。
例如:c 發光,其餘二極管不發光能夠用來表達一種字符。這種方案與上一行的方案能夠用來表示不一樣的字符,儘管看上去比較類似。
例如:a, b, c, d, e 發光,f, g 不發光能夠用來表達一種字符。
例如:b, f 發光,其餘二極管不發光則不能用來表達一種字符,由於發光的二極管沒有連成一片。
請問,小藍能夠用七段碼數碼管表達多少種不一樣的字符?




blog

【解題思路】

拿到題時,第一反應是深度優先搜索(dfs)。深搜大部分是用在樹型數據,而此題是一道圖論。不管搜索哪一個,深搜的最深點必定是7個燈全點亮。因此這就衍生出如何去重這一問題。
答主所採用的去重方法是:先把a~g用1-7表示。對於每次深搜的結果,例如abgcd點亮,其所對應的數字爲12347(數字升序排序),若ans[12347]=0則ans[12347]=1,且sum++。

【代碼實現】

#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
int book[8][8];
int bookk[10];
int ans[9999999];
int sum=0;
void dfs(int x){ 
	int i,k,n,p;
	for(i=1;i<=7;i++){ 
		if(bookk[i]==0&&book[x][i]==1){ 
			bookk[i]=1;
			n=0;
			p=0;
			for(k=7;k>=1;k--){ 
				if(bookk[k]==1){ 
					p+=k*pow(10,n);
					n++;
				}
			}
			if(ans[p]==0){ 
				ans[p]=1;
                sum++;
				//cout<<p<<endl;
			}
            dfs(i);
            bookk[i]=0;
		}
	}
}
int main(){ 
	memset(book,0,sizeof(book));
	memset(bookk,0,sizeof(bookk));
	book[1][2]=1;
	book[1][6]=1;
	book[2][1]=1;
	book[2][7]=1;
	book[2][3]=1;
	book[3][4]=1;
	book[3][2]=1;
	book[3][7]=1;
	book[4][3]=1;
	book[4][5]=1;
	book[5][4]=1;
	book[5][6]=1;
	book[5][7]=1;
	book[6][1]=1;
	book[6][5]=1;
	book[6][7]=1;
	book[7][2]=1;
	book[7][3]=1;
	book[7][5]=1;
	book[7][6]=1;
    for (int i=1; i<=7; i++) { 
        bookk[i]=1;
        sum++;
        dfs(i);
        bookk[i]=0;
    }
    cout<<sum<<endl;
}

答案:80。

F題 成績統計

【問題描述】

小藍給學生們組織了一場考試,卷面總分爲 100 分,每一個學生的得分都是
一個 0 到 100 的整數。
若是得分至少是 60 分,則稱爲及格。若是得分至少爲 85 分,則稱爲優秀。
請計算及格率和優秀率,用百分數表示,百分號前的部分四捨五入保留整
數。



【解題思路】

一道簡單題。用a、b記錄及格優秀人數,每次輸入數據時,判斷。最後計算百分率,注意四捨五入。

【代碼實現】

#include <iostream>
#include<iostream>
using namespace std;
int main(){ 
	int n,a=0,b=0,x;
	cin>>n;
	for(int i=1;i<=n;i++){ 
		cin>>x;
		if(x>=60){ 
			a++;
		}
		if(x>=85){ 
			b++;
		}
	}
	if(((a*100)%n)*10<n*5){ 
		cout<<a*100/n;
	}else{ 
		cout<<a*100/n+1;
	}
	cout<<'%'<<endl;
	if(((b*100)%n)*10<n*5){ 
		cout<<b*100/n;
	}else{ 
		cout<<b*100/n+1;
	}
	cout<<'%'<<endl;
	return 0;
}

G題 迴文日期

【問題描述】

2020 年春節期間,有一個特殊的日期引發了你們的注意:2020年2月2日。由於若是將這個日期按「yyyymmdd」 的格式寫成一個8 位數是20200202,
剛好是一個迴文數。咱們稱這樣的日期是迴文日期。
有人表示20200202 是「千年一遇」 的特殊日子。對此小明很不認同,由於不到2年以後就是下一個迴文日期:20211202 即2021年12月2日。
也有人表示20200202 並不只僅是一個迴文日期,仍是一個ABABBABA型的迴文日期。對此小明也不認同,由於大約100 年後就能遇到下一個ABABBABA 型的迴文日期:21211212 即2121 年12 月12 日。算不上「千年一遇」,頂多算「千年兩遇」。
給定一個8 位數的日期,請你計算該日期以後下一個迴文日期和下一個ABABBABA型的迴文日期各是哪一天



【解題思路】

一開始的想法是暴力計算,無限循環,每次將日期++,而後判斷是否迴文,是否合法日期(注意閏年)。而後運行時目測時間爆了。進行優化,每次將年份++,而後將總體變爲迴文,判斷是否合法日期。ABABBABA式迴文同理。

【代碼實現】

#include<iostream>
using namespace std;
char a[9];
int yy,mm,dd;
bool legal(){ 
	int y=(a[1]-'0')*1000+(a[2]-'0')*100+(a[3]-'0')*10+(a[4]-'0');
	if(y<=yy){ 
		return false;
	}
	int m=(a[5]-'0')*10+(a[6]-'0');
	if(y==yy&&m<=mm){ 
		return false;
	}
	int d=(a[7]-'0')*10+(a[8]-'0');
	if(y==yy&&m==mm&&d<=dd){ 
		return false;
	}
	if(m==1||m==3||m==5||m==7||m==8||m==10||m==12){ 
		if(d<=31){ 
			return true;
		}
	}
	if(m==4||m==6||m==9||m==11){ 
		if(d<=30){ 
			return true;
		}
	}
	if(y%400==0||(y%4==0&&y%100!=0)){ 
		if(m==2){ 
			if(d<=29){ 
				return true;
			}
		}
	}else{ 
		if(m==2){ 
			if(d<=28){ 
				return true;
			}
		}
	}
	return false;
}
void outt(){ 
	for(int i=1;i<=8;i++){ 
		cout<<a[i];
	}
	cout<<endl;
}
int main(){ 
	int i;
	a[0]='0';
	for(i=1;i<=8;i++){ 
		cin>>a[i];
	}
	yy=(a[1]-'0')*1000+(a[2]-'0')*100+(a[3]-'0')*10+(a[4]-'0');
	mm=(a[5]-'0')*10+(a[6]-'0');
	dd=(a[7]-'0')*10+(a[8]-'0');
	for(i=1;;){ 
		for(;i<=4;i++){ 
			a[9-i]=a[i];
		}
		if(legal()){ 
			outt();
			break;
		}
		for(i=4;i>=1;i--){ 
			if(a[i]=='9'){ 
				a[i]='0';
			}else{ 
				a[i]+=1;
				break;
			}
		}
	}
    for(i=1;;){ 
        a[3]=a[1];
        a[6]=a[1];
        a[8]=a[1];
        a[4]=a[2];
        a[5]=a[2];
        a[7]=a[2];
        if(legal()){ 
            outt();
            break;
        }
        if (a[2]=='9') { 
            a[1]+=1;
            a[2]='0';
        } else { 
            a[2]+=1;
        }
    }
}

H題 子串分值

【問題描述】

對於一個字符串 S,咱們定義 S 的分值 f (S ) 爲 S 中出現的不一樣的字符個
數。例如 f (「aba」) = 2, f (「abc」) = 3, f (「aaa」) = 1。
如今給定一個字符串 S [0::n − 1](長度爲 n),請你計算對於全部 S 的非空
子串 S [i:: j](0 ≤ i ≤ j < n), f (S [i:: j]) 的和是多少。


【解題思路】

動態規劃。dp[x]記錄從第一位至第x位的全部子串分值和。num[x][y]記錄從第x位到第y位的分值。
主要思想:
對於第x位字符c,
找到其最後出如今1~x-1的位置j。
若是k<=j,
num[k][x]=num[k][x-1]
不然
num[k][x]=num[k][x-1]+1;






dp[x]=dp[x-1]+num[1][x]+num[2][x]+…+num[x][x]

dp[n]爲最後結果。

【代碼實現】

#include <iostream>
using namespace std;
int dp[100005],num[100005][100005];
int main(){ 
    int i,j,k,n;
    char a[100005];
    cin>>a;
    for (i=0; ; i++) { 
        if (a[i]=='\0') { 
            break;
        }
    }
    n=i;
    for (; i>0; i--) { 
        a[i]=a[i-1];
    }
    for (i=1; i<=n; i++) { 
        dp[i]=1;
        num[i][i]=1;
    }
    for (i=2; i<=n; i++) { 
        for (j=i-1; j>=1; j--) { 
            if (a[j]==a[i]) { 
                break;
            }
        }
        for (k=1; k<i; k++) { 
            if (k<=j) { 
                num[k][i]=num[k][i-1];
            } else { 
                num[k][i]=num[k][i-1]+1;
            }
        }
        for (j=1,dp[i]=dp[i-1]; j<=i; j++) { 
            dp[i]+=num[j][i];
        }
    }
    cout<<dp[n]<<endl;
}

I題 平面切分

【問題描述】

平面上有 N 條直線,其中第 i 條直線是 y = Ai · x + Bi。
請計算這些直線將平面分紅了幾個部分。

【解題思路】

沒想出解題方法,感受有動態規劃的味道。目前想出的,對於每一條新增的直線,前序直線的交點是否在此線上,是否與某條線平行,分狀況討論。
挖坑,有待更新lalala

J題 字串排序

【問題描述】

小藍最近學習了一些排序算法,其中冒泡排序讓他印象深入。
在冒泡排序中,每次只能交換相鄰的兩個元素。小藍髮現,若是對一個字符串中的字符排序,只容許交換相鄰的兩個字符,則在全部可能的排序方案中,冒泡排序的總交換次數是最少的。
例如,對於字符串 lan 排序,只須要 1 次交換。對於字符串 qiao 排序,總共須要 4 次交換。
小藍找到了不少字符串試圖排序,他恰巧碰到一個字符串,須要 V 次交換,但是他忘了把這個字符串記下來,如今找不到了。
請幫助小藍找一個只包含小寫英文字母且沒有字母重複出現的字符串,對該串的字符排序,正好須要 V 次交換。若是可能找到多個,請告訴小藍最短的那個。若是最短的仍然有多個,請告訴小藍字典序最小的那個。請注意字符串中能夠包含相同的字符。



總結

第一次參賽。比賽的時候多是電腦有點問題,E題電腦沒有運行出結果,浪費了將近一個小時找錯,致使H題賽場有思路可是沒時間寫了,回寢室半個小時就寫出來H了。

相關文章
相關標籤/搜索