(水)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
#include
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
#include
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处理),因此我们需要枚举 2 25 2^{25} 225种情况(当然如果是程序设计题是超时的)。对每种情况勘测是否能管理整个方阵,可以则当前马放置的个数对其贡献加1。
可以使用DFS进行枚举,或者直接状态压缩成01表示。

#include
#include
#include
#include
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 
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
#include
#include
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 个字符串 s i s_i si,每个字符串的长度都不超过 10。
蒜头君在想,在这 n 个字符串中,以 s i s_i si为后缀的字符串有多少个呢?
输入格式
第一行输入一个整数 n。
接下来n行,每行输入一个字符串 s i s_i si
输出格式
输出n个整数,第 i 个整数表示以 s i s_i si为后缀的字符串的个数。
题解
把每个字符串当成一个26进制数。
因为有10个字符无法开辟如此大规模的数组,因此选择使用map容器存储(答案规模不会使用大太多空间,直接开辟数组会有很多内存损失)。
对每个字符串的所有后缀对应的26进制数贡献+1。最后输出答案即可。

#include
#include
#include
#include
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 个整数,每个整数 a i a_i ai表示每个人的体重。
输出格式
一个整数,表示最少需要购买的门票数目。
题解
对序列排序,之后由第n/2个元素开始对第n个元素进行检索,如果满足w[x]*2<=w[y],则对其标记。之后再有第n/2-1个元素,,,直到检索到第一个元素结束。
答案为(n/2+[n/2+1, n] (未被标记部分的和))

#include
#include
#include
#include
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
#include
#include
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 个整数 a i a_i ai ,表示每个纸团上面写的数字(这些纸团的输入顺序就是环形桌上纸团的摆放顺序)。
输出格式
输出一个整数,表示蒜头君最多能获取多少蒜币。
题解
贪心。
和HDU上的一道题很类似,不过这道题多了个环形。
ans枚举一遍序列,将序列每一个元素加上,判断若ans<=0,则将ans取0。
枚举完后的过程中的最大值即为连续串的最大值。
但因为增加了环形的条件,我们需要考虑去掉中间取首尾的情况。
这里,我们可以将序列复制倍增。然后当检测到我们增加了序列上所有元素,则定位到过程中得到值最小的位置,从该位置开始重新做一遍枚举。

#include
#include
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;
} 

你可能感兴趣的:(算法)