第十一屆藍橋杯大賽第二次模擬(軟件類C/C++)個人總結

題目鏈接

官網 輔導資料 2020年4月 本科組
在這裏插入圖片描述

填空題

1. 12.5MB

【解題思路】
單位換算
bit是位,B是字節,1B = 8bit,除此之外任意兩個都是1024的距離。
在這裏插入圖片描述
所以手機的4GB內存就是4230=2324*2^{30} = 2^{32}個字節(B)

【代碼】

//在計算機存儲中,12.5MB是多少字節?
#include<iostream>
using namespace std;
int main(){
	int res = 12.5*1024*1024;
	printf("%d", res);
	return 0;
}

【結果】

13107200

2. 最多邊數

【解題思路】

n個結點的有向邊,那麼每個結點都可以連接另外n-1個結點,總共n個結點,那麼總邊數是n(n1)n*(n-1)

【代碼】

//一個包含有2019個結點的有向圖,最多包含多少條邊?(不允許有重邊)
#include<iostream>
using namespace std;
int main(){
	int n = 2019;
	printf("%d", n*(n-1));
	return 0;
}

【結果】

4074342

3. 單詞重排

【解題思路】

STL的好處體現出來了。枚舉所有排列的另一個方法是從字典序最小排列開始,不停調用“求下一個排列”的過程。如何求下一個排列呢?C++的STL中提供了一個庫函數next_permutation。把所有排列都送到set集合中去重,最後輸出set的大小就行了。

比手算可快多了。

【代碼】

/*
將LANQIAO中的字母重新排列,可以得到不同的單詞。 
如LANQIAO、AAILNOQ等,注意這7個字母都要被用上,單詞不一定有具體的英文意義。
請問,總共能排列如多少個不同的單詞。
*/ 

#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
set<string> words;
string letters = "LANQIAO";
int main(){
	sort(letters.begin(), letters.end());
	do{
		words.insert(letters);
//		cout<<letters<<endl;
	}while(next_permutation(letters.begin(), letters.end()));
	cout<<words.size(); 
	return 0;
}

【結果】

2520

4. 括號序列

【解題思路】

這個n = 4,看着挺小的自己在草稿紙上枚舉都行。但是遇到這種題要想着怎麼用代碼解決,因爲往往真到考試的時候就是靠手寫解決不了的規模。

用遞歸的思想寫一個DFS,參數應該包括當前處理的位置idx,比如4對括號要填8處,已經使用的左括號的個數ln,已經使用的右括號數rn。

下一個狀態的判斷:

  • 只要表達式中左括號的個數大於等於右括號的個數,並且已經用的左括號沒超過可用的,就可以在idx處放左括號 ;
  • 只要表達式中右括號的個數小於左括號的個數,並且已經用的右括號沒超過可用的,就可以在idx處放右括號;

遞歸邊界的判斷:

  • 處理到了第2n2*n位,從第0位開始填,0~2n12*n-1剛好是2n2*n個符號。

【代碼】

/*
由1對括號,可以組成一種合法括號序列:()。
由2對括號,可以組成兩種合法括號序列:()()、(())。
由4對括號組成的合法括號序列一共有多少種?
*/ 

#include<iostream>
using namespace std;
int n = 4; // n表示左括號和右括號的個數 
int cnt = 0; //結果 
void dfs(int idx, int ln, int rn){
	// idx表示當前處理第幾位,ln、rn分別表示已經用了的左右括號個數 
	if(idx == 2*n){
		cnt++;
		return;
	}
	if( (ln >= rn) && (ln < n)){
		//只要表達式中左括號的個數大於等於右括號的個數,並且已經用的左括號沒超過可用的,就可以在idx處放左括號 
		dfs(idx+1, ln+1, rn); 
	}
	if( (rn < ln) && (rn < n)){
		//只要表達式中右括號的個數小於左括號的個數,並且已經用的右括號沒超過可用的,就可以在idx處放右括號 
		dfs(idx+1, ln, rn+1);
	}
}
int main(){
	dfs(0, 0, 0); 
	printf("cnt:%d", cnt);
	return 0;
}

【結果】

cnt:14

編程題

5. 反倍數

【解題思路】

放心枚舉。

【代碼】

/*
給定三個整數 a, b, c:
如果一個整數既不是 a 的整數倍也不是 b 的整數倍還不是 c 的整數倍。 
則這個數稱爲反倍數,請問在 1 至 n 中有多少個反倍數。
*/ 
#include<iostream>
using namespace std;
int main(){
	int n;
	scanf("%d", &n);
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	int ans = 0;
	for(int i=1; i<=n; i++){
		if(i%a==0 || i%b==0 || i%c==0){
			continue;
		}
		else{
			ans++;
		}
	}
	printf("%d", ans);
	return 0;
}

6. 凱撒加密

【解題思路】

這種在一定範圍內循環的感覺就要善用%。

【代碼】

/*
給定一個單詞,請使用凱撒密碼將這個單詞加密。
凱撒密碼是一種替換加密的技術,單詞中的所有字母都在字母表上向後偏移3位後被替換成密文。
即a變爲d,b變爲e,...,w變爲z,x變爲a,y變爲b,z變爲c。
例如,lanqiao會變成odqtldr。
*/ 
#include<iostream>
#include<cstring> 
using namespace std;
int main(){
	string str;
	cin>>str;
	for(int i=0; i<str.length(); i++){
		char ch = (str[i]-'a'+3) % 26 + 'a';
		cout<<ch;
	}
	return 0;
}

7. 螺旋

【解題思路】

螺旋填數,這類題經常遇到。首先我們要確定並維持一個方向,就是當前填數的方向,然後朝這個方向填到底了,就轉90°繼續填。

本題中可以用四個整數0, 1, 2, 3分別表示右下左上,初始方向d = 0,然後d方向填到底,就順時針變換d++,因爲一直這麼順時針的轉,剛好是d+1(d+1)%44
朝着當前方向d走一步,經常用增量矩陣

int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 右下左上

爲了旋轉中判斷哪些框填過數了,用一個bool型的數組checked表示當前位置的框子是否有數了,那麼思路就清楚了,直接看代碼:

【代碼】

#include<iostream>
using namespace std;
const int maxn = 1010;
int num[maxn][maxn];
bool check[maxn][maxn] = {false};
int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 右下左上 
int main(){
	int n, m;
	scanf("%d%d",&n, &m);
	int pos_x, pos_y;
	scanf("%d%d", &pos_x, &pos_y); 
	int x=1, y=1, times = 0;
	// 0, 1, 2, 3分別表示右下左上
	int d = 0; 
	int number = 1;
	while(times < n*m){
		if(!check[x][y]){
			num[x][y] = number;
			number++;
			check[x][y] = true;
			times++;
		}
		// 如果朝着d方向走一步不可行,就順時針改變方向。 
		if(x+direction[d][0] == n+1 || y+direction[d][1] == m+1 || x+direction[d][0] == 0 || y+direction[d][1] == 0){
			d = (d+1)%4;
		} 
		else if(check[x+direction[d][0]][y+direction[d][1]]){
			d = (d+1)%4;
		}
		if(x == pos_x && y == pos_y){
			break;
		} 
		// 更新位置 
		x += direction[d][0];
		y += direction[d][1];
	}
	printf("%d", num[pos_x][pos_y]);
	return 0;
}

8. 擺動序列

【解題思路】

方法一:按照題意直接遞歸。當然過不了所有的樣例,但是思想簡單。

【代碼】

#include<iostream>
using namespace std;
const int maxn = 1010;
const int MOD = 10000;
int mem[maxn][maxn];
int m, n;
long long f(int cur, int pos){
//	printf("%d %d\n", cur, pos);
	if(mem[cur][pos] != 0){
		return mem[cur][pos];
	} 
	if(pos == m) return 1;
	long long ans = 0;
	if(pos%2){//cur在奇數列 
		for(int i=1; i<cur; i++){
			ans = (ans + f(i, pos+1))%MOD;
		} 
	}
	else{
		for(int i=cur+1; i<=n; i++){
			ans = (ans + f(i, pos+1))%MOD;
		}
	}
	mem[cur][pos] = ans;
	return ans;
}
int main(){
	scanf("%d%d", &m, &n);
	long long ans = 0;
	for(int i=2; i<=n; i++){
		ans = (ans + f(i, 1)) % MOD;
	}
	printf("%lld", ans);
	return 0;
} 

在這裏插入圖片描述
方法二:爲了達到更多分,每次只拆開一層。

【代碼】

#include<iostream>
using namespace std;
const int maxn = 1010;
const int MOD = 10000;
int mem[maxn][maxn];
int m, n;
long long f(int cur, int pos){
//	printf("%d %d\n", cur, pos);
	if(mem[cur][pos] != 0){
		return mem[cur][pos];
	} 
	if(pos == m) return 1;
	long long ans = 0;
	if(pos%2){
		//表示下一個數爲1~cur-1的序列個數
		if(cur == 1) return 0;
		//只拆一層,拆開下一個數爲cu1-1的一層 
		return mem[cur][pos] = (f(cur-1, pos) + f(cur-1, pos+1))%MOD;
	}
	else{
		//表示下一個數爲cur+1~n的序列個數
		if(cur == n) return 0;
		//拆開下一個數爲cur+1的一層 
		return mem[cur][pos] = (f(cur+1, pos) + f(cur+1, pos+1))%MOD;
	}
}
int main(){
	scanf("%d%d", &m, &n);
	long long ans = 0;
	ans = f(1, 0);
	printf("%lld", ans);
	return 0;
} 

9. 通電

prim算法,最小生成樹,模板題。

【代碼】

#include<iostream>
#include<cmath> 
using namespace std;
const int maxn = 1010;
const double INF = 0x3fffffff;
double G[maxn][maxn];
struct node{
	int x, y, h;
}graph[maxn];
int n;
double d[maxn];
bool vis[maxn];
double prim(){
	fill(d, d+maxn, INF);
	d[0] = 0;
	double ans = 0;
	for(int i=0; i<n; i++){
		int u = -1;
		double MIN = INF;
		for(int j=0; j<n; j++){
			if((!vis[j]) && d[j] < MIN){
				u = j;
				MIN = d[j];
			}
		} 
		if(u == -1) return -1;
		vis[u] = true;
		ans += d[u];
		for(int v=0; v<n; v++){
			if( (!vis[v]) && u != v && G[u][v] < d[v]){
				d[v] = G[u][v];
			}
		}
	}
	return ans;
}
int main(){
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		scanf("%d%d%d", &graph[i].x, &graph[i].y, &graph[i].h);
	} 
	for(int i=0; i<n-1; i++){
		for(int j=i+1; j<n; j++){
			double	x = pow(graph[i].x - graph[j].x, 2);
			double	y = pow(graph[i].y - graph[j].y, 2);
			double	h = pow(graph[i].h - graph[j].h, 2);
			G[i][j] = G[j][i] = sqrt(x+y)+h;
		}
	}
//	for(int i=0; i<n; i++){
//		for(int j=0; j<n; j++){
//			printf("%.2f ", G[i][j]);
//		}
//		printf("\n");
//	}
	double ans = prim();
	printf("%.2f", ans);
	return 0;
}
/*
4
1 1 3
9 9 7
8 8 6
4 5 4
0.000000 27.313708 18.899495 6.000000
27.313708 0.000000 2.414214 15.403124
18.899495 2.414214 0.000000 9.000000
6.000000 15.403124 9.000000 0.000000
*/

10. 植樹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章