2019年第十屆藍橋杯C/C++ B組省賽題解

試題A——組隊

在這裏插入圖片描述
在這裏插入圖片描述
話說這道題目別看簡單,其實還真有點坑點,不細心一點,必然會WA,我比賽的時候,可能腦子抽了算的一行的最大值,其實這一道題目是要你選出1 - 20個編號的球員中選出五個位置的人,使得1號位到5號位的最大值儘可能大。就相當於求1號位到5號位每一列都最大的值。
在這裏插入圖片描述
492填上?那就WA了
可以看到我們圈出的每一個位置的最大值1號位和3號位還有4號位都是同一個人,這顯然是錯的。
所以有一個限制條件就是每個人只能去一個位置而不是多個位置。
在這裏插入圖片描述
這道題目編程實現的話還不如直接算來的直接。
答案:490

試題B——年號字串

在這裏插入圖片描述
這道題目實際就是一個將十進制轉換爲26進制,分析題目給的數據
A——1
1 * 26^0 = 1
Z——26
26 * 26^0 = 26
AA——27
1 * 26^1 + 1 * 26^0 = 26 + 1 = 7
AB——28
1 * 26^1 + 2 * 26^0 = 26 + 2 = 28
AZ——52
1 * 26^1 + 26 * 26^0 = 26 + 26 = 52
LQ——329
12 * 26^1 + 17 * 26^0 = 312 + 17 = 329
這樣就可以推出來是10進制轉換26進制了
然後我們模擬將十進制轉換爲26進制即可,使用短除法,在逆序輸出字符串即可。

#include <bits/stdc++.h>
using namespace std;

char str[27] = {0,'A','B','C','D','E','F','G','H','I','J','K'
   			,'L','M','N','O','P','Q','R','S','T','U','V',
   			'W','X','Y','Z'};

int main() {
   int num;
   string ans = "";
   scanf("%d", &num);
   while(num) {
   	ans += str[num % 26];
   	num /= 26;
   }
   for (int i = ans.size() - 1; i >= 0; i--) {
   	cout << ans[i];
   }
   return 0;
}

答案:BYQ

試題C——數列求值

在這裏插入圖片描述
這道題目其實就是一道斐波那契數列變形,而且題目直接告訴你從第四項開始每一項等於前三項的和,那根本不用觀察直接可以寫出遞推式:

dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] (i >=4)
//邊界條件
dp[1] = dp[2] = dp[3] = 1

知道這個就可以很好的求出答案了。
但是這麼大的數肯定超出long long 了,我們可以猜一下使用高精度?不用使用,對每一步mod10000即可得到正確答案,那麼很多人會問每一步mod不會對結果有影響?
證明:
在這裏插入圖片描述
字寫的有點醜,這樣可以證明其實每一次加起來在mod10000和加起來全部mod10000結果相同,那麼就好算了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e4;

LL dp[20190325];

int main() {
	dp[1] = dp[2] = dp[3] = 1;
	for (int i = 4; i <= 20190324; i++) {
		dp[i] = (dp[i - 1] + dp[i - 2] + dp[i - 3]) % mod;
	}
	cout << dp[20190324] << endl;
	return 0;
}

答案:4659

試題D——數的分解

在這裏插入圖片描述
這道題目因爲分解三個數然後不能重複,還有每個數不包含2,4。其實這道題目不用搜索,枚舉就可以了,但是重複情況怎麼考慮?
我們發現2019 = x + y + z
那麼對於一組x,y,z三個數的位置不同也算一種那麼,對於x,y,z三個位置相當於有6種擺放位置,我們只需要將枚舉結果除以6即可得到答案。

#include <bits/stdc++.h>
using namespace std;

bool check(int x, int y, int z) {	//判斷三個正整數中是否含2或4 
	int res = 0;
	while (x) {
		res = x % 10;
		if (res == 2 || res == 4) return false;
		x /= 10;
	} 
	while (y) {
		res = y % 10;
		if (res == 2 || res == 4) return false;
		y /= 10;
	}
	while (z) {
		res = z % 10;
		if (res == 2 || res == 4) return false;
		z /= 10;
	}
	return true;
}

int main() {
	int ans = 0;
	for (int a = 1; a < 2019; a++) {
		for (int b = 1; b < 2019; b++) {
			if (b == a) continue;		//a,b,c三個數不相同
			for (int c = 1; c < 2019; c++) {
				if (b == c || a == c) continue;
				if (a + b + c == 2019 && check(a, b, c)) ans++;
			} 
		}
	}
	cout << ans / 6 << endl;
	return 0;
}

因爲是填空題,時間複雜度爲2019^3,還是要跑個10幾秒的
答案:40785

試題E——迷宮

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
當時看到這道題目,欣喜若狂,因爲我之前在刷藍橋杯的題庫的時候在算法提高裏面刷到了這道題目,一模一樣,名字叫學霸的迷宮

其實這道題目看到最短路,最容易想到BFS(廣搜),但是這道題目不是要你求最短路長度而是求出最短路的路徑,提交一個字符串,其實別想差,最簡單的就是將方向數組的每一步上,下,左,右,和D,U,L,R相對應,只需要在結構體中添加一個字符串記錄即可。方向數組的順序要按照字典序從小到大的順序D,L,R,U來跑。

/*
測試數據
30 50 
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
*/
#include <bits/stdc++.h>
using namespace std;

char mp[30][50];	//地圖
bool vis[30][50];	//標記該點是否走過
int dir[4][2] = {{1,0},{0,-1},{0,1},{-1,0}};	//方向數組按照下,左,右,上的順序走
char dirc[4] = {'D','L','R','U'}; 
int n,m;  	//迷宮的行列

struct node{
	int x;	//橫座標 
	int y;	//縱座標 
	int step;	//步數 
	string str;	//路徑 
	node(int xx, int yy, int ss, string s) {	//構造函數 
		x = xx;
		y = yy;
		step = ss;
		str = s;
	}
}; 

queue<node> q; //創建隊列

bool check(int x, int y) {	//判斷是否越界以及是否是牆以及是否訪問過了 
	if (x < 0 || x >= n || y < 0 || y >= m || vis[x][y] || mp[x][y] == '1') {
		return false;
	}
	return true;
}

void bfs(int x, int y) {
	q.push(node(x, y, 0, ""));
	vis[x][y] = true;
	while (!q.empty()) {
		node now = q.front();
		if (now.x == n - 1 && now.y == m - 1) {	//到達終點了 
			cout << now.str << endl;
			cout << now.step << endl;
			break;
		}
		q.pop();
		for (int i = 0; i < 4; i++) {
			int nx = now.x + dir[i][0];
			int ny = now.y + dir[i][1];
			if (check(nx, ny)) {
				q.push(node(nx, ny, now.step + 1, now.str + dirc[i]));
				vis[nx][ny] = true;
			}
		}
	}
} 

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++) {
		scanf("%s", mp[i]);
	}
	bfs(0, 0);
	return 0;
}



答案:DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

試題F——特別數的和

在這裏插入圖片描述
在這裏插入圖片描述
這道題目差不多是水題,沒啥算法,直接枚舉加check即可

#include <bits/stdc++.h>
using namespace std;

bool check(int x) {
	int res = 0;
	while (x) {
		res = x % 10;
		if (res == 0 || res == 1 || res == 2 || res == 9) return true;
		x /= 10;
	}
	return false;
}

int main() {
	int n;
	scanf("%d", &n);
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (check(i)) {
			ans += i;
		}
	}
	cout << ans << endl;
	return 0;
}

7.試題G——完全二叉樹的權值

在這裏插入圖片描述
在這裏插入圖片描述
這道題目剛開始在考場上看到的時候以爲要構建一顆樹的,哪知道也是水題
其實就是求每一層的和的最大值,至於最大值相同時輸出最小的那一層,根本不要考慮,因爲你找到最大值以後你就拿後面層數的權值和和最大值比較只要小於就不更新,那麼層數也不會更新。這樣就可以了,我是邊輸入,邊處理的,因爲是二叉樹那麼我除了第一層輸入一個數,然後我們將長度*2,那麼長度就爲2代表第二層需要輸入2個數,然後每次邊輸入,邊處理即可。完全二叉樹不是滿二叉樹,最後一層不一定滿。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

int main()
{
    int n;
    cin >> n;
    LL maxv = INT_MIN;	//INT_MIN是在limits.h頭文件中,代表INT型最小值 ,這裏記錄權值和最大 
	LL maxv_d = 0;	//最大的權值和的層數 
    for (int i = 0, length = 1, depth = 1; i < n; depth++, length *= 2)
    {
        LL sum = 0;	//每一層的和 
        for (int j = 0; j < length && i < n; j++, i++ )
        {
            int x;
            cin >> x;
            sum += x;
        }

        if (sum > maxv)
        {
            maxv = sum;
            maxv_d = depth;
        }
    }

    cout << maxv_d << endl;

    return 0;
}

試題H——等差數列

在這裏插入圖片描述
這道題目考了一點數論知識就是gcd最大公約數,等差數列公式.
因爲我們需要求出最短的等差數列並且包含所給出的數,那麼我們就要求出最大的公差,首先看樣例可以推出數列的第一項就是最小的數,數列的末項就是最大的數,我們只需要求出最大的公約數就能得到最短的長度的等差數列。
我們將每一個數減去第一個數,然後依次找最大公約數,然後通過等差數列的公式即可得到最短的長度。
等差數列公式a[n] - a[1] / d + 1即爲求出的等差數列長度。
坑點:
注意如果所給數據相同時,應該返回長度n

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;

LL a[maxn];

int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	sort(a + 1, a + n + 1);
	for (int i = 2; i <= n; i++) {
		a[i] -= a[1];
	}
	int d = a[2];
	for (int i = 3; i <= n; i++) {
		d = gcd(d, a[i]);
	}
	if (d == 0) {
		cout << n << endl;
	} else {
		cout << a[n] / d + 1 << endl;
	}
	return 0;
}

因爲之前我對每個數都減掉了a[1]所以最後不用減掉a[1]了。

試題I——後綴表達式

在這裏插入圖片描述
這道題目是個巨坑啊,不知道怎麼說好呢,在用例這裏故意卡了個剛好和中綴表達式算出來結果一樣的樣例來,讓人按照中綴表達式來算,全部先排序然後拿大的相加再減掉小的。(本人已入坑)
其實樣例中綴表達式來算和後綴表達式來算結果是一樣的。
反例:
0 2
1 2 3
我們如果按照之前想的中綴的話,從大到小排序3 - 2 - 1 = 0
後綴表達式計算 3 -(1 - 2) = 4,其實後綴表達式可以相當於加括號後計算。
其實最大值後綴表達式相當於所有的加號相加,然後再加上剩下的數(除了最小的數以外), 再減掉最小的數。
其實看成一棵樹來計算就行。
在這裏插入圖片描述
減號相當於一個二叉樹的根。

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int maxn = 200010;

int n, m;
int a[maxn];

int main()
{
    scanf("%d%d", &n, &m);
    int k = n + m + 1;
    LL sum = 0;
    for (int i = 0; i < k; i++)
    {
        scanf("%d", &a[i]);
        sum += a[i];	//求這些數的和 
    }
    sort(a, a + k);

    if (a[0] >= 0)	//第一個數大於0,說明沒有負數,因爲之前加過一次a[0],然後我們本來就需要減掉a[0],所以減掉2*a[0] 
    {
        if (m) sum -= 2 * a[0];
    }
    else		//如果是負數的那麼我們需要加上,因爲之前是負數,加上去也就相當於減掉,所以-=2*a[i](a[i]<0) 
    {
        for (int i = 0; i < k && a[i] < 0 && m > 0; i++ )
        {
            sum -= a[i] * 2;
            m-- ;
        }
    }

    cout << sum << endl;
    return 0;
}

試題J——靈能傳輸

在這裏插入圖片描述
在這裏插入圖片描述
其實這道題目代碼比較簡單,思路比較難想就是思想 + 貪心。這道題目沒怎麼弄明白,有老師說了這道題目,給出鏈接,和他的代碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <limits.h>

using namespace std;

typedef long long LL;
const int N = 300010;

int n;
LL sum[N], a[N], s0, sn;
bool st[N];

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%lld", &sum[i]);
            sum[i] += sum[i - 1];
        }

        s0 = sum[0], sn = sum[n];
        if (s0 > sn) swap(s0, sn);

        sort(sum, sum + n + 1);

        for (int i = 0; i <= n; i ++ )
            if (s0 == sum[i])
            {
                s0 = i;
                break;
            }
        for (int i = n; i >= 0; i -- )
            if (sn == sum[i])
            {
                sn = i;
                break;
            }

        memset(st, 0, sizeof st);
        int l = 0, r = n;
        for (int i = s0; i >= 0; i -= 2)
        {
            a[l ++ ] = sum[i];
            st[i] = true;
        }
        for (int i = sn; i <= n; i += 2)
        {
            a[r -- ] = sum[i];
            st[i] = true;
        }
        for (int i = 0; i <= n; i ++ )
            if (!st[i])
            {
                a[l ++ ] = sum[i];
            }

        LL res = 0;
        for (int i = 1; i <= n; i ++ ) res = max(res, abs(a[i] - a[i - 1]));
        cout << res << endl;
    }
    return 0;
}

視頻講解:靈能傳輸

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