(水)2019 藍橋杯省賽 B 組模擬賽(一)

結果填空:鐘錶

一天蒜頭君 22:28:45 開始睡覺,06:24:26 醒來之後,蒜頭君在想,今天我睡了多久?
請你告訴蒜頭君睡了"h : m : s",如果 h,m,s不足兩位時,前面補0。
例如:蒜頭君睡了 10小時 1 分鐘 0 秒,那麼請輸出"10:01:00"(不包含引號)。
題解
妥妥大水題,手推結果"07:55:41"

結果填空:青蛙爬井

有一口深度爲 high 米的水井,井底有一隻青蛙,它每天白天能夠沿井壁向上爬 up 米,夜裏則順井壁向下滑 down 米。
若青蛙從某個早晨開始向外爬,當 high = 60405,up = 105,dow = 35,計算青蛙多少天能夠爬出井口?
注意:不能簡單地認爲每天上升的高度等於白天向上爬的距離減去夜間下滑的距離,因爲若白天能爬出井口,則不必等到晚上。
題解
簡單模擬

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
	int h = 60405, up = 105, down = 35, day = 0;
	while(true){
		h-=up;
		day+=1;
		if(h <= 0){
			printf("%d\n", day);
			return 0;
		}
		h+=down;
	}
	return 0;
} 

結果填空:倍數

一天蒜頭君在想,[l,r] 之間有多少個數字是 dd 的倍數呢?
但是區間 [l,r] 是 d 的倍數的數字太多,於是聰明的蒜頭君便找到了你。
當 l = 1032,r = 12302135942453,d = 234,d 的倍數有多少個呢?
題解
我們很容易可以知道在區間[1,x]內滿足是d的倍數的數字的個數爲(x/d)
所以結果易求得(r/d-(l-1)/d)

#include<iostream>
#include<cstdio>
using namespace std;
int main(){
	long long l = 1031, r = 12302135942453;
	printf("%lld\n", r/234-l/234);
	return 0;
}

結果填空:馬的管轄

在中國象棋中,馬是走日字的。一個馬的管轄範圍指的是當前位置以及一步之內能走到的位置,下圖的綠色旗子表示馬能走到的位置。
在這裏插入圖片描述
如果一匹馬的某個方向被蹩馬腳,它就不能往這個方向跳了,如下圖所示,海星的位置存在旗子,馬就不能往上跳到那兩個位置了:
在這裏插入圖片描述
那麼問題來了,在一個 n×m 的棋盤內,如何用最少的馬管轄住所有 n×m 個格子。比如 n=m=3 時,最少要用 55 只馬才能管轄所有棋盤,一種可能的方案如下:
在這裏插入圖片描述
當 n=m=5 時,請你求出用最少馬管轄的 方案個數。
題解
因爲是填空題沒有時間限制,我們可以直接枚舉結果。(心虛
5*5的方陣每一個點只存在放馬或者不放馬的可能(即可以當成0和1處理),因此我們需要枚舉2252^{25}種情況(當然如果是程序設計題是超時的)。對每種情況勘測是否能管理整個方陣,可以則當前馬放置的個數對其貢獻加1。
可以使用DFS進行枚舉,或者直接狀態壓縮成01表示。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
int map[5][5];
int dir[8][2] = {-1, -2, -1, 2, -2, -1, -2, 1, 1, -2, 1, 2, 2, -1, 2, 1};
int Dir[8][2] = { 0, -1,  0, 1, -1,  0, -1, 0, 0, -1, 0, 1, 1,  0, 1, 0};
int f(int n){
	int cnt = 0;
	memset(map, 0, sizeof(map));
	for(int i = 0; i < 5; i++){
		for(int j = 0; j < 5; j++){
			map[i][j] = n&1;
			cnt+=map[i][j];
			n>>=1;
		}
	}
	for(int i = 0; i < 5; i++){
		for(int j = 0; j < 5; j++){
			if(map[i][j]==1){
				for(int k = 0; k < 8; k++){
					int x = i+dir[k][0], y = j+dir[k][1];
					int tx = i+Dir[k][0], ty = j+Dir[k][1];
					if(x < 0 || x >= 5 || y < 0 || y >= 5) continue;
					if(map[tx][ty]==1) continue;
					if(map[x][y]==0) map[x][y] = 2;
				}
			}
		}
	}
	for(int i = 0; i < 5; i++){
		for(int j = 0; j < 5; j++){
			if(!map[i][j]) return INF;
		}
	}
	return cnt;
}
int main(){
	int Dp[26];
	memset(Dp, 0, sizeof(Dp));
	int cnt = 0;
	for(int i = 0; i < (1<<25); i++){
		int k = f(i);
		if(k!=INF) Dp[k]++;
	}
	int Min = INF;
	for(int i = 0; i < 26; i++){
		if(Dp[i]){
			printf("%d\n", Dp[i]);
			return 0;
		}
	}
	return 0;
} 

代碼填空:LIS

LIS 是最長上升子序列。什麼是最長上升子序列? 就是給你一個序列,請你在其中求出一段最長嚴格上升的部分,它不一定要連續。
就像這樣:2, 3, 4, 7 和 2, 3, 4, 6 就是序列 2 5 3 4 1 7 6 的兩個上升子序列,最長的長度是 4。
題解
就是LIS的板子題,不懂可以看看CSDN上大佬寫的O(nlogn)複雜度的LIS詳解。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 9;
int f[N], a[N];
int n;
int find(int l, int r, int x) {
	while (l < r) {
		int mid = (l + r) / 2;
		if (f[mid] < x) {
			l = mid + 1;
		} else {
			r = mid;
		}
	}
	return l;
}
int lis() {
	int len = 0;
	for (int i = 0; i < n; i++) {
		int k = find(0, len, a[i]);
		f[k] = a[i];
		if (k == len) {
			len++;
		}
	}
	return len;
}
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", a + i);
	}
	printf("%d\n", lis());
	return 0;
}

程序設計:找質數

一天蒜頭君猜想,是不是所有的偶數(除了 2),都可以用兩個質數相加得到呢?於是聰明的蒜頭君就找你來驗證了。
輸入格式
第一行輸入一個整數 t 表示測試組數。
接下來 t 行,每行一個整數 n。
輸出格式
輸出兩個整數,因爲答案可能有多個,所有要求輸出的這兩個整數是所有答案中字典序最小的。
題解
看到質數,先做一遍篩法。
接着發現數據量挺大的,每次枚舉必然超時,所以預處理一遍結果即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e6+10;
int prim[maxn], w[maxn], cnt = 0;
int x[maxn];
void init(){
	memset(x, 0, sizeof(x));
	memset(prim, false, sizeof(prim));
	for(int i = 2; i < maxn; i++){
		if(prim[i]) continue;
		w[cnt++] = i;
		for(int j = i << 1; j < maxn; j+=i) prim[j] = true;
	}
	x[4] = 2;
	for(int i = 6; i < maxn; i+=2){
		for(int j = 1; j < cnt && i > w[j]; j++){
			if(!prim[i-w[j]]){
				x[i] = w[j];
				break;
			}
		}
	}
}
int main(){
	init();
	int T;
	scanf("%d", &T);
	while(T--){
		int n;
		scanf("%d", &n); 
		printf("%d %d\n",x[n], n-x[n]);
	}
	return 0;
} 

程序設計:後綴字符串

一天蒜頭君得到 n 個字符串 sis_i,每個字符串的長度都不超過 10。
蒜頭君在想,在這 n 個字符串中,以 sis_i爲後綴的字符串有多少個呢?
輸入格式
第一行輸入一個整數 n。
接下來n行,每行輸入一個字符串 sis_i
輸出格式
輸出n個整數,第 i 個整數表示以 sis_i爲後綴的字符串的個數。
題解
把每個字符串當成一個26進制數。
因爲有10個字符無法開闢如此大規模的數組,因此選擇使用map容器存儲(答案規模不會使用大太多空間,直接開闢數組會有很多內存損失)。
對每個字符串的所有後綴對應的26進制數貢獻+1。最後輸出答案即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int maxn = 1e5+10;
char s[maxn][11];
map<long long, int> hashMap;
int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%s", s[i]);
		int len = strlen(s[i]);
		long long ans = 0;
		for(int j = len-1; j >= 0; j--){
			ans = ans*26+s[i][j]-96;
			if(hashMap.find(ans) == hashMap.end()){
				hashMap[ans] = 1;
			}
			else{
				hashMap[ans]+=1;
			}
		}
	}
	for(int i = 0; i < n; i++){
		int len = strlen(s[i]);
		long long ans = 0;
		for(int j = len-1; j >= 0; j--){
			ans = ans*26+s[i][j]-96;
		}
		printf("%d\n", hashMap[ans]);
	}
	return 0;
} 

程序設計:輕重搭配

n 個同學去動物園參觀,原本每人都需要買一張門票,但售票處推出了一個優惠活動,一個體重爲 x 的人可以和體重至少爲 2x 配對,這樣兩人只需買一張票。現在給出了 n 個人的體重,請你計算他們最少需要買幾張門票?
輸入格式
第一行一個整數 n,表示人數。
第二行 n 個整數,每個整數 aia_i表示每個人的體重。
輸出格式
一個整數,表示最少需要購買的門票數目。
題解
對序列排序,之後由第n/2個元素開始對第n個元素進行檢索,如果滿足w[x]*2<=w[y],則對其標記。之後再有第n/2-1個元素,,,直到檢索到第一個元素結束。
答案爲(n/2+[n/2+1, n] (未被標記部分的和))

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 5e5+10;
int w[maxn];
bool vis[maxn];
int main(){
	memset(vis, false, sizeof(vis));
	int n, cnt = 0;
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%d", &w[i]);
	}
	sort(w, w+n);
	int k = n-1;
	for(int i = n/2-1; i >= 0; i--){
		if(w[k] >= 2*w[i]){
			vis[k] = true;
			k-=1;
		}
		cnt+=1;
	}
	for(int i = n/2; i < n; i++)if(!vis[i])cnt++; 
	printf("%d\n", cnt);
	return 0;
} 

程序設計:摳圖

題解
噁心的大模擬題,跟着題目寫就行,沒什麼難度

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int map[550][550];
int x1[550], y1[550], x2[550], y2[550], cnt;
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		int n, m;
		cnt = 0;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				if(j!=m)
					scanf("%d\t", &map[i][j]);
				else
					scanf("%d", &map[i][j]);
			}
		}
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				if(!map[i][j]){
					bool vis = false;
					for(int k = 0; k < cnt; k++){
						if(x1[k] <= i && i <= x2[k] && y1[k] <= j && j <= y2[k]){
							vis = true;
							break;
						}
					}
					if(vis) continue;
					int X1 = i, Y1 = j;
					int X2, Y2;
					int X3, Y3;
					for(X2 = X1; X2 <= n; X2++){
						if(map[X2][Y1])break;
					}
					X2-=1;
					if(X2==X1) continue;
					for(Y2 = Y1; Y2 <= m; Y2++){
						if(map[X2][Y2])break;
					}
					Y2-=1;
					if(Y2==Y1) continue;
					for(X3 = X2; X3 > 0; X3--){
						if(map[X3][Y2])break;
					}
					X3+=1;
					if(X3!=X1) continue;
					for(Y3 = Y2; Y3 > 0; Y3--){
						if(map[X3][Y3])break;
					}
					Y3+=1;
					if(Y3!=Y1) continue;
					x1[cnt] = X1, y1[cnt] = Y1, x2[cnt] = X2, y2[cnt] = Y2;
					cnt+=1;
				}
			}
		}
		for(int i = 1; i <= n; i++){
			for(int j = 1; j <= m; j++){
				if(map[i][j]){
					bool vis = false;
					for(int k = 0; k < cnt; k++){
						if(x1[k] <= i && i <= x2[k] && y1[k] <= j && j <= y2[k]){
							vis = true;
							break;
						}
					}
					if(!vis) map[i][j] = 0;
				}
				j==m?printf("%d\n", map[i][j]):printf("%d ", map[i][j]);
			}
		}
	}
	return 0;
} 

程序設計:蒜廠年會

在蒜廠年會上有一個抽獎,在一個環形的桌子上,有 n 個紙團,每個紙團上寫一個數字,表示你可以獲得多少蒜幣。但是這個遊戲比較坑,裏面竟然有負數,表示你要支付多少蒜幣。因爲這些數字都是可見的,所以大家都是不會出現的賠的情況。
遊戲規則:每人只能抓一次,只能抓取一段連續的紙團,所有紙團上的數字和就是你可以獲得的蒜幣。
蒜頭君作爲蒜廠的一員在想,我怎麼可以獲得最多的蒜幣呢?最多能獲取多少蒜幣呢?
因爲年會是發獎,那麼一定有大於 0 的紙團。
輸入格式
第一行輸入一個整數 n,表示有 n 個紙團。
第二行輸入輸入 n 個整數 aia_i ,表示每個紙團上面寫的數字(這些紙團的輸入順序就是環形桌上紙團的擺放順序)。
輸出格式
輸出一個整數,表示蒜頭君最多能獲取多少蒜幣。
題解
貪心。
和HDU上的一道題很類似,不過這道題多了個環形。
ans枚舉一遍序列,將序列每一個元素加上,判斷若ans<=0,則將ans取0。
枚舉完後的過程中的最大值即爲連續串的最大值。
但因爲增加了環形的條件,我們需要考慮去掉中間取首尾的情況。
這裏,我們可以將序列複製倍增。然後當檢測到我們增加了序列上所有元素,則定位到過程中得到值最小的位置,從該位置開始重新做一遍枚舉。

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1e5+10;
const long long INF = 0x3f3f3f3f3f3f3f3f;
long long w[maxn<<1];
long long p[maxn<<1];
int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i++){
		scanf("%lld", &w[i]);
		w[i+n] = w[i];
	}
	long long Max = 0, val = 0, cnt = 0;
	for(int i = 0; i < 2*n; i++){
		val+=w[i], cnt+=1;
		if(cnt > n){
			int node = i;
			long long Min = INF;
			for(int j = i-n; j < i; j++){
				if(Min > p[j]){
					node = j;
					Min = p[j];
				}
			}
			i = node;
			val = 0, cnt = 0;
		}
		if(val <= 0){
			cnt = 0,val = 0;
		}
		p[i] = val;
		Max = max(Max, val);
	}
	printf("%lld\n", Max);
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章