前言
最近參加了藍橋杯比賽,現在把題目貼上來
轉載:https://blog.csdn.net/jziwjxjd/article/details/109136535
試題A 門牌制作
【問題描述】
小藍要為一條街的住戶制作門牌號。
這條街一共有 2020 位住戶,門牌號從 1 到 2020 編號。
小藍制作門牌的方法是先制作 0 到 9 這幾個數字字符,最后根據需要將字
符粘貼到門牌上,例如門牌 1017 需要依次粘貼字符 1、 0、 1、 7,即需要 1 個
字符 0, 2 個字符 1, 1 個字符 7。
請問要制作所有的 1 到 2020 號門牌,總共需要多少個字符 2?
【答案提交】
這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。
答案:624
試題B 既約分數
【問題描述】
如果一個分數的分子和分母的最大公約數是1,這個分數稱為既約分數。例如,3/4 , 5/2 , 1/8 , 7/1都是既約分數。請問,有多少個既約分數,分子和分母都是1 到2020 之間的整數(包括1和2020)?
【答案提交】
這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。
答案:2481215
試題C 蛇形填數
【問題描述】
如下圖所示,小明用從1 開始的正整數“蛇形”填充無限大的矩陣。
容易看出矩陣第二行第二列中的數是5。請你計算矩陣中第20 行第20 列的數是多少?
【答案提交】
這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一
個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。
答案:761
#include <stdio.h>
int a[100][100];
int cnt=1;
int main()
{
int i;
int x,y;
for(i = 1 ; i <= 40; i++)
{
if(i % 2==1 )
{
for(x = i, y = 1; x >= 1 && y <= i; x--, y++)
a[x][y] = cnt++;
}
else
{
for(int x = 1, y = i; x <= i && y >= 1; x++, y--)
a[x][y] = cnt++;
}
}
printf("%d\n", a[20][20]);
return 0;
}
試題D 跑步鍛煉
【問題描述】
小藍每天都鍛煉身體。
正常情況下,小藍每天跑 1 千米。如果某天是周一或者月初(1 日),為了
激勵自己,小藍要跑 2 千米。如果同時是周一或月初,小藍也是跑 2 千米。
小藍跑步已經堅持了很長時間,從 2000 年 1 月 1 日周六(含)到 2020 年
10 月 1 日周四(含)。請問這段時間小藍總共跑步多少千米?
【答案提交】
這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一
個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。
答案:8879
試題E 七段碼
【問題描述】
小藍要用七段碼數碼管來表示一種特殊的文字。
七段碼上圖給出了七段碼數碼管的一個圖示,數碼管中一共有7 段可以發光的二極管,分別標記為a, b, c, d, e, f, g。小藍要選擇一部分二極管(至少要有一個)發光來表達字符。在設計字符的表達時,要求所有發光的二極管是連成一片的。
例如:b 發光,其他二極管不發光可以用來表達一種字符。
例如:c 發光,其他二極管不發光可以用來表達一種字符。這種方案與上一行的方案可以用來表示不同的字符,盡管看上去比較相似。
例如:a, b, c, d, e 發光,f, g 不發光可以用來表達一種字符。
例如:b, f 發光,其他二極管不發光則不能用來表達一種字符,因為發光的二極管沒有連成一片。
請問,小藍可以用七段碼數碼管表達多少種不同的字符?
【答案提交】
這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一
個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。
答案:80
此題解來自:https://blog.csdn.net/weixin_43989094/article/details/109149148
//a-0,b-1,c-2,d-3,e-4,f-5,g-6
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
int ve[7][7];
bool visit[7];
int ans=0;
set<set<int> > se;
void dfs(int x,set<int> s)
{
if(!se.count(s))
{
se.insert(s);
// set<int>::iterator it;
// for(it=s.begin();it!=s.end();it++)
// {
// cout<<char('a'+*it)<<" ";
// }
// cout<<endl;
ans++;
}
if(s.size()==7)
return ;
for(int j=0;j<7;j++)
{
if(visit[j]||!ve[x][j])
continue;
s.insert(j);
visit[j]=1;
dfs(j,s);
visit[j]=0;
s.erase(j);
}
}
void add(int x,int y)
{
ve[x][y]=1;
ve[y][x]=1;
}
int main()
{
//a的所有邊
add(0,1);
add(0,5);
//b的所有邊
add(1,6);
add(1,2);
//c的所有邊
add(2,6);
add(2,3);
//d的所有邊
add(3,4);
//e的所有邊
add(4,5);
add(4,6);
//f的所有邊
add(5,6);
set<int> s;
for(int i=0;i<=6;i++)
{
s.insert(i);
visit[i]=1;
dfs(i,s);
visit[i]=0;
s.erase(i);
}
cout<<ans<<endl;
}
程序設計題
試題F 成績統計
【問題描述】
小藍給學生們組織了一場考試,卷面總分為 100 分,每個學生的得分都是
一個 0 到 100 的整數。
如果得分至少是 60 分,則稱為及格。如果得分至少為 85 分,則稱為優秀。
請計算及格率和優秀率,用百分數表示,百分號前的部分四舍五入保留整
數。
【輸入格式】
輸入的第一行包含一個整數 n,表示考試人數。
接下來 n 行,每行包含一個 0 至 100 的整數,表示一個學生的得分。
【輸出格式】
輸出兩行,每行一個百分數,分別表示及格率和優秀率。百分號前的部分
四舍五入保留整數。
【樣例輸入】
7
80
92
56
74
88
100
0
【樣例輸出】
71%
43%
【評測用例規模與約定】
對於50% 的評測用例, 1 ≤ n ≤ 100。
對於所有評測用例,1 ≤ n ≤10000。
答案
#include<stdio.h>
int main()
{
double a1=0,a2=0;
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++){
int s;
scanf("%d",&s);
if(s>=60) a1++;
if(s>=85) a2++;
}
a1=(a1/n+0.005)*100;
a2=(a2/n+0.005)*100;
printf("%d%%\n%d%%",(int)a1,(int)a2);
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型的回文日期各是哪一天。
【輸入格式】
輸入包含一個八位整數N,表示日期。
【輸出格式】
輸出兩行,每行1 個八位數。第一行表示下一個回文日期,第二行表示下
一個ABABBABA 型的回文日期。
【樣例輸入】
20200202
【樣例輸出】
20211202
21211212
【評測用例規模與約定】
對於所有評測用例,10000101 ≤ N ≤ 89991231,保證N 是一個合法日期的8位數表示。
答案
#include <iostream>
#include <iomanip>
using namespace std;
int a[12]={31,28,31,30,31,30,31,31,30,31,30,31};
bool isok(int x)//判斷閏年
{
if( x%400==0 ) return true;
if( x%100==0 ) return false;
if( x%4==0 ) return true;
return false;
}
int main()
{
int x;
cin >> x;
int year=x/10000;//輸入的年
int month=x%10000/100;//輸入的月
int day=x%100;//輸入的日
int flag=0;
int m=year%10*10+year%100/10;//年份對應的回文月
int n=year/100%10*10+year/1000;//年份對應的回文日
if(isok(year)) a[1]=29;
else a[1]=28;
if((m>month&&m<13)||(m==month&&n>day&&n<=a[m-1])){//當年對應的回文日期在輸入的日期之后
cout<<setw(2)<<setfill('0')<<year<<setw(2)<<setfill('0')<<m<<setw(2)<<setfill('0')<<n<<endl;//輸出當年對應的回文日期
flag=1;
if((year/1000!=year%10)&&(year%100==m%10*10+m/10)&&(m==n)){ //判斷當年對應的回文日期是否是ABAB BABA型日期
cout<<setw(2)<<setfill('0')<<year<<setw(2)<<setfill('0')<<m<<setw(2)<<setfill('0')<<n<<endl;
return 0;
}
}
while(year<=8999){
year++;
month=year%10*10+year%100/10;//年份回文月
day=year/100%10*10+year/1000;//年份回文日
if(month>12||day>31) continue;
if(isok(year)) a[1]=29;
else a[1]=28;
if((flag!=1)&&month>0&&month<13&&day>0&&day<=a[month-1]){//找回文型
cout<<setw(2)<<setfill('0')<<year<<setw(2)<<setfill('0')<<month<<setw(2)<<setfill('0')<<day<<endl;
flag=1;
}
if((year/1000!=year%10)&&(year%100==month%10*10+month/10)&&(month==day)){//找ABABBABA型
cout<<setw(2)<<setfill('0')<<year<<setw(2)<<setfill('0')<<month<<setw(2)<<setfill('0')<<day<<endl;
break;
}
}
return 0;
}
試題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]) 的和是多少。
【輸入格式】
輸入一行包含一個由小寫字母組成的字符串S。
【輸出格式】
輸出一個整數表示答案。
【樣例輸入】
ababc
【樣例輸出】
28
【樣例說明】
子串 f值
a 1
ab 2
aba 2
abab 2
ababc 3
b 1
ba 2
bab 2
babc 3
a 1
ab 2
abc 3
b 1
bc 2
c 1
【評測用例規模與約定】
對於20% 的評測用例,1 ≤ n ≤ 10;
對於40% 的評測用例,1 ≤ n ≤ 100;
對於50% 的評測用例,1 ≤ n ≤ 1000;
對於60% 的評測用例,1 ≤ n ≤ 10000;
對於所有評測用例,1 ≤ n ≤ 100000。
答案
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int id[27][maxn],nu[27],b[27];//下標
char a[maxn];
long long ans;
int main()
{
cin >> (a+1);
int len=strlen(a+1);
for(int i=1;i<=len;i++)
id[a[i]-'a'][++nu[a[i]-'a']]=i;//記錄每個字母出現的下標
for(int i=1;i<=len;i++)//計算以i開頭的子串的貢獻
{
int top=0;
for(int j=0;j<=25;j++)//記錄每個字母最快出現在i之后的下標
{
if( id[j][ nu[j] ] >= i )//假如出現最晚的這個字母比i大才去查找,而且需要是第一次出現
{
int index = lower_bound(id[j],id[j]+1+nu[j],i)-id[j];//二分查找加速
b[++top] = id[j][index];
}
}
sort(b+1,b+1+top);//對每個字母的出現時間排序
int last = i;
for(int j=2;j<=top;j++)
{
ans += ( b[j]-last )*(j-1) ;
last = b[j];
}
ans += ( len-last+1 )*top;
}
cout << ans;
}
試題I 平面切分
【問題描述】
平面上有 N 條直線,其中第 i 條直線是 y = Ai · x + Bi。
請計算這些直線將平面分成了幾個部分。
【輸入格式】
第一行包含一個整數 N。
以下 N 行,每行包含兩個整數 Ai; Bi。
【輸出格式】
一個整數代表答案。
【樣例輸入】
3
1 1
2 2
3 3
【樣例輸出】
6
【評測用例規模與約定】
對於 50% 的評測用例, 1 ≤ N ≤ 4, −10 ≤ Ai; Bi ≤ 10。
對於所有評測用例, 1 ≤ N ≤ 1000, −100000 ≤ Ai; Bi ≤ 100000。
答案
此題解來自:https://blog.csdn.net/weixin_43989094/article/details/109149148
#include<iostream>
#include<cstring>
#include<set>
using namespace std;
set<pair<double,double> > se;
const int N = 1005;
double A[N];
double B[N];
set<pair<double,double> > s;
set<pair<double,double> >::iterator it;
int main()
{
int n,i,j,x,y;
cin>>n;
for(i=0;i<n;i++)
{
cin>>x>>y;
s.insert(make_pair(x,y));
}
n = s.size();
for(i=0,it=s.begin(),it++;it!=s.end();it++,i++)
{
A[i]=(*it).first;
B[i]=(*it).second;
}
long long ans=2;
for(i=1;i<n;i++)
{
set<pair<double,double> > se;
for(j=i-1;j>=0;j--)
{
double x=(B[j]-B[i])/(A[i]-A[j]);
double y=(A[j]*B[i]-A[i]*B[j])/(A[j]-A[i]);
se.insert(make_pair(x,y));
}
int n2=se.size();
ans+=(n2+1);
}
cout<<ans<<endl;
}
試題J 字串排序
【問題描述】
小藍最近學習了一些排序算法,其中冒泡排序讓他印象深刻。
在冒泡排序中,每次只能交換相鄰的兩個元素。小藍發現,如果對一個字符串中的字符排序,只允許交換相鄰的兩個字符,則在所有可能的排序方案中,冒泡排序的總交換次數是最少的。
例如,對於字符串 lan 排序,只需要 1 次交換。對於字符串 qiao 排序,總共需要 4 次交換。
小藍找到了很多字符串試圖排序,他恰巧碰到一個字符串,需要 V 次交換,可是他忘了把這個字符串記下來,現在找不到了。
請幫助小藍找一個只包含小寫英文字母且沒有字母重復出現的字符串,對該串的字符排序,正好需要 V 次交換。如果可能找到多個,請告訴小藍最短的那個。如果最短的仍然有多個,請告訴小藍字典序最小的那個。請注意字符串中可以包含相同的字符。
【輸入格式】
輸入的第一行包含一個整數V,小藍的幸運數字。
【輸出格式】
題面要求的一行字符串。
【樣例輸入】
4
【樣例輸出】
bbaa
【樣例輸入】
100
【樣例輸出】
jihgfeeddccbbaa
【評測用例規模與約定】
對於 30% 的評測用例, 1 ≤ V ≤ 20。
對於 50% 的評測用例, 1 ≤ V ≤ 100。
對於所有評測用例, 1 ≤ V ≤ 10000。
題解一
此題解來自https://blog.csdn.net/jziwjxjd/article/details/109136535
(代碼在部分數據是錯的,只能算騙分代碼,寫錯了…)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
char ans[N], res[N];
int n, len;
bool judge()
{
int i = len;
while(ans[i] == res[i] && i) i--;
return res[i] < ans[i];
}
void dfs(int now, int maxn, int m, int sum) {
if(sum == n)
{
if(m < len || (m == len && judge()))
{
len = m;
for(int i = 1; i <= len; i++) ans[i] = res[i];
}
return;
}
if(now >= 26) return ;
for(int i = 1; i <= maxn; i++)
{
int temp = sum + m * i;
if(temp > n) return ;
res[m + i] = char(now + 'a');
dfs(now + 1, i, m + i, temp);
}
}
int main()
{
len = 0x3f3f3f3f;
scanf("%d", &n);
dfs(0, 8, 0, 0);
for(int i = len; i >= 1; i--)
putchar(ans[i]);
return 0;
}
題解二
此題解來自https://blog.csdn.net/calculate23/article/details/109139748
由於限定的的合法字符只有 26 個字母,假設沒有這個約束可以直接按完全逆序排序使得長度最小,然后在消去部分逆序數。而對於這題,首先字母是可以重復的,並且在長度相同若要保證字典序更小顯然要讓較大的字母數量較少,此時只要讓較小的字母較多即可。
這里可能會陷入一個可貪心的思維誤區:這也是我下面展示的一個假算法的例子:錯誤點在字典序最優是一個全局的特性,局部字典序最優不能保證全局字典序最優,所以正解應該是搜索+剪枝。
假算法
#include <bits/stdc++.h>
using namespace std;
const int N = (int)1e4+5;
int num[N];
int main() {
int n, m;
int _max, id, len, sum;
scanf("%d", &n);
sum = 0; len = 0;
while (sum < n) {
id = 1;
for (int i = 2; i <= 26; i++) { //找到s最小的點, 如果存在多個取字典序更小的
if (num[i] < num[id]) id = i;
}
sum = sum + len - num[id];
len ++;
num[id] ++;
}
m = sum - n; //注意更新逆序數差值
for (int i = 1; i <= 26; i++) {
if (num[i]) {
_max = i;
}
}
for (int i = _max; i >= 1; i--) {
for (int j = 0; j < num[i]; j++) {
printf("%c", 'a'+i-1);
}
}
printf("\n");
for (int i = 1; i <= m; i++) {
/*
//盡量消去最大的數字,並且逆序數變化得最少,如果變化量相同則選擇字典序較小的方案
id = 0; sum = INT_MAX;
for (int j = 1; j <= 26; j++) {
if (m - i + num[_max] - num[j] >= 0) {
if (sum > num[j] - num[_max] + 1) {
sum = num[j] - num[_max] + 1;
id = j;
}
}
}
if (id) {
num[id] ++;
num[_max] --;
if (!num[_max]) _max--;
i += sum - 1;
printf("~%d\n", id);
continue;
}
*/
for (int j = _max; j >= 1; j--) {
id = 0;
while(num[++id]!= num[j]);
if (id != j) {
num[id] ++;
num[j] --;
break;
}
}
if (!num[_max]) {
_max--;
}
}
for (int i = _max; i >= 1; i--) {
for (int j = 0; j < num[i]; j++) {
printf("%c", 'a'+i-1);
}
}
printf("\n");
return 0;
}
搜索+剪枝
#include <bits/stdc++.h>
using namespace std;
const int N = (int)1e4+5;
int num[N], res[N];
int n, m, _max, len;
bool judge(int letter) {
int i = 26, j = letter;
while (!res[i]) i--;
if (i == j) {
while (i > 0 && j > 0) {
if (res[i] != num[j]) {
return res[i] > num[j];
} else {
i--; j--;
}
}
}
return i > j;
}
void dfs(int letter, int curlen, int cursum, int l) {
if (cursum > n) return ;
if (letter > _max) return ;
if (curlen > len) return ;
if (curlen == len && cursum != n) return ;
if (letter == _max && cursum != n) return ;
if (cursum == n) {
if (curlen < len || judge(letter)) { //長度減小或字典序減小更新結果
len = curlen;
for (int i = 1; i <= 26; i++) {
res[i] = num[i];
}
}
return ;
}
for (int i = 1; i <= l; i++) {
num[letter + 1] = i;
dfs(letter + 1, curlen + i, cursum + i * curlen, i);
}
num[letter + 1] = 0;
}
int main() {
scanf("%d", &n);
m = 0; len = 0;
while (m < n) {
int id = 1;
for (int i = 2; i <= 26; i++) { //找到s最小的點, 如果存在多個取字典序更小的
if (res[i] < res[id]) id = i;
}
m += len - res[id];
_max = max(_max, id);
len ++; res[id] ++;
}
dfs(0, 0, 0, 10);
for (int i = _max; i >= 1; i--) {
for (int j = res[i]; j > 0; j--) {
printf("%c", i-1+'a');
}
}
printf("\n");
return 0;
}