牛客小白月赛31(B D E G H I J 题解)

B、 A+B (用矩阵代表字符)

原题链接:https://ac.nowcoder.com/acm/contest/10746/B

题目描述
将数字以及加号用字符矩阵的形式进行表示,对应关系如下:
在这里插入图片描述
以字符形式给出一个运算式,请你计算结果,同时也请你将结果按照字符的形式输出

输入描述
第一行给出一个正整数 t , 1 ≤ t ≤ 50,代表测试数据的组数
每两组测试数据之间以一个空白行分隔
每组测试数据共有五行字符串表示一个字符串,字符串长度均不大于500且五行长度相等
算式中只会出现数字和加号,保证输入的算式可计算且小于2 ^ 31

输出描述
每组测试数据输出5行字符串表示运算结果,且每两个输出用一个空行隔开

示例
在这里插入图片描述
在这里插入图片描述

思路

一开始没发现每3列会用1个“ . ”分隔开,一直没看明白样例。
后面就用数组模拟栈,先将字符形式的运算式转变成对应含义的数字和加号存在ch数组里面。
再将得到的数字累加到ans里面,此时ans的数值就是运算结果。
但是题目要求将结果以字符矩阵输出,此时要注意输出和输入一样,每隔3列要输出1个“ . ”分隔开

代码

//B 
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}
char book[11][5][3];

//将0~9和+的字符矩阵分别存放在三维数组book里面
void makeTable() {
    
    
	char temp[11][5][3] = {
    
    {
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '#', '#', '#'}, {
    
    '#', '.', '.'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '#', '.', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '.'}, {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '.'}, {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '.', '#'}, {
    
    '.', '.', '#'}, {
    
    '.', '.', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}}, 
							{
    
    {
    
    '#', '#', '#'}, {
    
    '#', '.', '#'}, {
    
    '#', '#', '#'}, {
    
    '.', '.', '#'}, {
    
    '#', '#', '#'}},
							{
    
    {
    
    '.', '.', '.'}, {
    
    '.', '#', '.'}, {
    
    '#', '#', '#'}, {
    
    '.', '#', '.'}, {
    
    '.', '.', '.'}}};
	for (int i = 0; i < 11; ++ i) {
    
    
		for (int j = 0; j < 5; ++ j) {
    
    
			for (int k = 0; k < 3; ++ k) {
    
    
				book[i][j][k] = temp[i][j][k];
			}
		}
	}
}

//将最终的运算结果以字符矩阵的形式输出
void output(int num) {
    
    
	int arr[15] = {
    
    1};
	int k = 0;
	if (num == 0) {
    
    
		for (int i = 0; i < 5; ++ i) {
    
    
			printf("%c%c%c\n", book[0][i][0], book[0][i][1], book[0][i][2]);
		}
	}
	else {
    
    
		//先将每一个位数取出
		while(num > 0) {
    
    
			arr[k ++] = num % 10;
			num /= 10;
		}
		//按行输出
		for (int i = 0; i < 5; ++ i) {
    
    
			for (int j = k - 1; j >= 0; -- j) {
    
    
				if (j == k - 1) 
					printf("%c%c%c", book[arr[j]][i][0], book[arr[j]][i][1], book[arr[j]][i][2]);
				else printf(".%c%c%c", book[arr[j]][i][0], book[arr[j]][i][1], book[arr[j]][i][2]);
			}
			printf("\n");
		}
			
	}
	
}


int main() {
    
    
	makeTable();
	
	int t;
	scanf("%d", &t);
	while (t --) {
    
    
		string str[5];
		for (int i = 0; i < 5; ++ i) cin>>str[i];
		
		int length = str[0].size(), k = 0;
		//字符矩阵转运算式
		char ch[200] = {
    
    '1'};
		for (int i = 0; i < length; i += 4) {
    
    
			if (str[2][i] == '#' && str[2][i + 1] == '.' && str[3][i] == '#' && str[3][i + 1] == '.')
				ch[k ++] = '0';
			else if (str[0][i] == '.' && str[0][i + 1] == '.' && str[0][i + 2] == '#')
				ch[k ++] = '1';
			else if (str[3][i] == '#' && str[3][i + 1] == '.' && str[3][i + 2] == '.')
				ch[k ++] = '2';
			else if (str[1][i] == '.' && str[2][i] == '#' && str[3][i] == '.' && str[3][i + 1] == '.')
				ch[k ++] = '3';
			else if (str[0][i] == '#' && str[0][i + 1] == '.' && str[0][i + 2] == '#')
				ch[k ++] = '4';
			else if (str[3][i] == '.' && str[3][i + 1] == '.' && str[1][i + 2] == '.')
				ch[k ++] = '5';
			else if (str[1][i + 1] == '.' && str[1][i + 2] == '.' && str[3][i + 1] == '.')
				ch[k ++] = '6';
			else if (str[2][i] == '#' && str[2][i + 1] == '.' && str[2][i + 2] == '#' && str[3][i] == '.')
				ch[k ++] = '7';
			else if (str[3][i] == '#' && str[3][i + 1] == '.' && str[3][i + 2] == '#')
				ch[k ++] = '8';
			else if (str[3][i] == '.' && str[3][i + 1] == '.' && str[3][i + 2] == '#')
				ch[k ++] = '9';
			else ch[k ++] = '+';
		}
		
		//计算运算结果
		int num = 0, ans = 0;
		for (int i = 0; i < k; ++ i) {
    
    
			if (ch[i] == '+') {
    
    
				ans += num;
				num = 0;
			}
			else  num = num * 10 + ch[i] - '0';
		}
		ans += num;

		output(ans);
		getchar();
		printf("\n");
	}
	return 0;
}

D - 坐标计数 ((x⊕y,∣x−y∣)后x 和 y都为0)

原题链接:https://ac.nowcoder.com/acm/contest/10746/D

题目描述
定义一个坐标变换,坐标 (x,y) 变换后变为(x⊕y,∣x−y∣)。
给定一片矩形区域,计算区域内有多少个整数点在经过有限次变换后变为 (0,0)。

输入描述
输入第一行一个数字 t, 1 ≤ t ≤ 50,表示测试数据组数
接下来一行四个数字 1 <= x1 , y1 , x2 , y2 <= 10 ^ 5 代表给出的矩形区域
(x1 , y1)为矩形区域左下角,(x2 , y2)表示矩形右上角,包含边界上的点。

输入保证有 x1 < x2 , y1 < y2

输出描述
输出区域内满足变换要求的整数点个数

示例
在这里插入图片描述

思路

这道题观察样例或者打表都可以发现:矩形面积内的所有点在有限次变换后都能变为(0,0)。

现证明如下:

要使(x,y)在有限次(x⊕y,∣x−y∣)变换中变为(0,0),那么首先,什么时候 x⊕y 会等于 0 ?只有 x == y 的时候。而当 x == y 时,∣x−y∣ 也会等于 0。那么现在问题就转变为有没有可能在有限次变换后 x == y

但是呢,当把 x 和 y 当成二进制串来考虑相关运算的时候,无论是 x⊕y 还是 ∣x−y∣,如果32位上的二进制数字全都一起考虑相不相等的话,那无疑是比较麻烦的。
那么这里我们可以想,能不能每次先从最后一位比起,假如相同,那么通过右移运算(即:取半)不断缩减,直到 x == y 或者进入死循环。

那么这个时候我们就可以对 x 和 y 进行分类讨论,进而发现:
① 当 x 和 y 同为奇数时,x⊕y 后 x ’ 为偶数,∣x−y∣后 y ’ 也为偶数;
② 当 x 和 y 同为偶数时,x⊕y 后 x ’ 为偶数,∣x−y∣后 y ’ 也为偶数;
③ 当 x 和 y 奇偶性不同时,x⊕y 后 x ’ 为奇数,∣x−y∣后 y ’ 也为奇数,此时便转变为情况①的类型。

综合上面三种情况,我们会发现,无论一开始 x 和 y 奇偶性如何,有限次(x⊕y ,∣x−y∣)后 x ’ 和 y '最终都能成为偶数。而因为我们的目的是要比较 x == y 能不能成立,即:x 和 y 的二进制串各个位数会不会完全相同。那么对于偶数而言,任何偶数最右边的二进制数都为0,进而 x == y 能不能成立就转变为 x / 2 == y / 2能不能成立

至于 x / 2 和 y / 2 的奇偶性又可以通过有限次(x⊕y ,∣x−y∣)又变为两个都是偶数,循环往复,一直对 x 和 y 取一半,最终 x 和 y 都会变成 0

至此证明完毕。
结论:直接计算给定的矩形面积内的点的数目。

代码

//D
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

int main() {
    
    
	int t;
	scanf("%d", &t);
	while (t --) {
    
    
		ll a, b, c, d;
		scanf("%lld %lld %lld %lld", &a, &b, &c, &d);
		ll ans = (c - a + 1) * (d - b + 1);
		printf("%lld\n", ans);
	}
	return 0;
}

E - 解方程 (ax+by=xy)

原题链接:https://ac.nowcoder.com/acm/contest/10746/E

题目描述
给出两个正整数 a,b,计算满足方程 ax+by=x*y 的正整数(x, y) 的组数。

输入描述
输入的第一行有一个正整数 t 测试数据的组数。
每组测试数据在一行中给出两个正整数 a, b。
1 ≤ t ≤ 10 ^ 3
1 ≤ a, b ≤ 10 ^ 6

输出描述
输出一个数字表示答案
保证答案小于2 ^ 31

示例
在这里插入图片描述
说明
对于第一组满足条件的(x,y)为(3,3),(4,2)
对于第二组满足条件的(x,y)为(4,8),(5,5),(6,4),(9,3)

思路

ax + by = xy
ax + by + ab = xy + ab
ab = xy + ab - ax - by
ab = (y - a)(x - b)

所以可以发现,要求方程解的个数,其实就是找ab的因子个数。比如说ab = 18,那么(y - a) 对应的可以取到1,2,3,6,9,18,当(y - a)确定时,只有唯一一个(x - b)与之对应,因为a、b是确定的,所以每一组x和y也是唯一的(即不会出现重复)。
那么还有一个问题就是,有没有可能出现 (y - a)= -3, (x - b)= -6 的情况?
乍一看好像可以,但其实不用考虑负数的情况
比如说a = 3, b = 6,那么ab = (y - a)(x - b)使得要么x = y = 0,要么xy < 0(即x和y有一个为负数),这和题意要求的xy均为正整数矛盾。
综上,答案即ab因子总数

为了避免超时,这里用了约数个数定理求ab的因子总数。
在这里插入图片描述
简单来说,每一个正整数都可以分解成若干个质数相乘,那么对于18 = 2 * 3 * 3,可以看出18由1个2和2个3相乘得到,那么18的因子个数就是(1 + 1)* (2 + 1)= 6个。

代码

//E 
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}

const int N = 1000030;
//线性筛法求素数 
int primes[N], cnt;     // primes[]存储所有素数,从0开始存 
bool st[N];         // st[x]存储x是否被筛掉,素数--false,合数--true 
void get_primes(int n)
{
    
    
    for (int i = 2; i <= n; ++ i)
    {
    
    
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n / i; j ++ )
        {
    
    
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int main() {
    
    
	int t;
	scanf("%d", &t);
	
	get_primes(N - 3);  //做素数表
	
	while (t --) {
    
    
		ll a, b;
		scanf("%lld %lld", &a, &b);
		
		ll num = a * b;
		int ans = 1;
		if (num == 1)  printf("%lld\n", ans);
		else {
    
    
			int k = 0;
			//用约数个数定理求因子个数
			while (num > 1) {
    
    
				if (num % primes[k] == 0) {
    
    
					int cnt = 0;
					while (num % primes[k] == 0) {
    
    
						++ cnt;
						num /= primes[k];
					}
					ans *= (cnt + 1);
				}
				++ k;
			}
			printf("%lld\n", ans);
		}
	}
	return 0;
}

G - 简单题的逆袭 (x ^ k ≤ y 求max(k))

原题链接:https://ac.nowcoder.com/acm/contest/10746/G

题目描述
给定x,y,找出满足方程 x ^ k ≤ y 的最大的 k

输入描述
第一行一个 t, 1 ≤ t ≤ 300,代表测试数据的组数
每组输入只有一行,包含两个整数 x,y
0 ≤ x, y ≤ 10 ^ 18

输出描述
每个测试数据在一行中输出一个整数k,若k不存在或者无限大,输出 “-1”

示例
在这里插入图片描述
思路
先对 x == 0、 x == 1、 y == 0的特殊情况进行判断,再直接试除法去计算 k 的最大值。

y == 0 :指数函数的函数值必定大于等于0,所以 y 不能为0;
x == 0:假设 y = 0,那么 k 可无限大,假如 y > 0,则 k 不存在;
x == 1:假设 y 取非 1 的值,k 不存在;假设 y 取 1,那么 k 可无限大。

代码

//G
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

#define getlength(array,len) {len = (sizeof(array) / sizeof(array[0]));}

int main() {
    
    
	int t;
	scanf("%d", &t);
	while(t --) {
    
    
		ll x, y;
		scanf("%lld %lld", &x, &y);
		if (x == 0 || y == 0 || x == 1) printf("%d\n", -1);
		else {
    
    
			ll cnt = 0;
			ll a = x;
			while (x <= y) {
    
    
				++ cnt;
				y /= a;

			}
			printf("%lld\n", cnt);
		} 
	}
	return 0;
}

H - 对称之美 (字符串数组能否回文)

原题链接:https://ac.nowcoder.com/acm/contest/10746/H

题目描述
给出n个字符串,从第1个字符串一直到第n个字符串每个串取一个字母来构成一个新字符串,新字符串的第i个字母只能从第i行的字符串中选出,这样就得到了一个新的长度为n的字符串,请问这个字符串是否有可能为回文字符串?

输入描述
第一行一个数字 t , 1 ≤ t ≤ 50, 代表测试数据的组数
每组测试数据先给出一个数字 n,然后接下来n行每行一个只由小写字母组成的字符串 si
1 ≤ n ≤ 100, 1 ≤ ∣s i ∣ ≤ 50

输出描述
在一行中输出 “Yes” or “No”

示例
在这里插入图片描述

思路

头尾双指针 i 和 j 分别指向第 i 和倒数第 i 个字符串,也就是需要对应的能构成回文字符的那两行,分别用数组记录这两个字符串各个字符的数目(这里需要注意的是,这两个字符串可能不一定等长)。
记录完毕后,从 0 到 25 遍历是否有数量同时大于 0 的字符,没有则提前结束循环,有则 ++ i 、-- j 接着判断,直到扫描完字符数组或者提前break;
(开一个length == 26的数组,刚好可以存下每个字符的数目,字符对应的索引是:char - ‘a’)

代码

//H
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

string arr[110];

int main() {
    
    
	int t;
	scanf("%d", &t);
	while (t --) {
    
    
		int n;
		scanf("%d", &n);
		for (int i = 0; i < n; ++ i) {
    
    
			cin>> arr[i];
		} 
		int i, j;
		for (i = 0, j = n -1; i < j; ++ i, -- j) {
    
      //判断第i和第j行是否有相同的字符
			int book1[26] = {
    
    0}, book2[26] = {
    
    0}; //用book记录这两行各个字母出现的次数
			// i 行
			int length = arr[i].size();
			for (int k = 0; k < length; ++ k) {
    
    
				++ book1[arr[i][k] - 'a'];
			}
			//j行
			length = arr[j].size();
			for (int k = 0; k < length; ++ k) {
    
    
				++ book2[arr[j][k] - 'a'];
			}
			int k;
			for (k = 0; k < 26; ++ k) {
    
    
				if (book1[k] > 0 && book2[k] > 0) break; //两行中有相同的字符就break
			}
			if (k == 26) break; //没找到相同字符,就提前跳出最外层循环,说明此时构不成回文串了
		}
		if (n == 1) printf("Yes\n");
		else {
    
    
			if (i < j) printf("No\n");  // i < j 说明提前结束循环
			else printf("Yes\n");
		}
	}
	return 0;
}

I - 非对称之美 (最长非回文子串)

原题链接:https://ac.nowcoder.com/acm/contest/10746/I

题目描述
给出一个字符串,求最长非回文子字符串的长度

输入描述
在一行中给出一个字符串 s, 1≤∣s∣≤ 10 ^ 7

输出描述
一个整数

示例
在这里插入图片描述

思路

不难发现,最长非回文字符串的长度只有 0 ,size() - 1,size() 三种。详细参见代码注释:

代码

// I 
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long 

string str;
int main() {
    
    
	cin>> str;
	int l = 0, r = str.size() - 1;
	if (r == 0) printf("%d", 0); //只有一个字符也是回文串
	else {
    
    
		char c = str[0];
		bool bo = true;
		
		while (l <= r) {
    
    
			if (str[l] != c || str[r] != c) //判断字符串会不会全是同一个字符
				bo = false;  
			if (str[l] == str[r]) {
    
    
				++ l;
				-- r;
			}
			else break;
		}
		// 本身是回文串,且由同一个字符构成
		if (l > r && bo) printf("0\n");
		// 本身是回文串,但不只由一个字符
		else if (l > r) printf("%lld\n", str.size() - 1);
		// 本身不是回文串
		else printf("%lld\n", str.size());
	}
	return 0;
}

J - 排列算式

原题链接:https://ac.nowcoder.com/acm/contest/10746/J

题目描述
给出n个数字,对于这些数字是否存在一种计算顺序,使得计算过程中数字不会超过3也不会小于0?

输入描述
首行给出一个正整数 t (1 ≤ t ≤ 1000)代表测试数据组数
每组测试数据第一行一个正整数 n ( 1 ≤ n ≤ 500)
第二行包含n个以空格分隔的数字
输入保证每一个数字都是 −3, −2, −1, +0, +1, +2, +3 的其中一个。

输出描述
每组测试数据输出一行,“Yes” or “No”

示例
在这里插入图片描述

说明
第一组依照 +3,−2,+2,−1 的顺序由左至右计算总和,过程会依序算得 3, 1, 3, 2,满足题目要求。
很显然第二组不存在满足要求的计算顺序

思路

因为题目给出的限制是计算过程在【0,3】之间,那么如果 n 个数里面没有 3 或者 -3,只有 ± 1 和 ± 2,那么只要这 n 个数的总和 0 <= sum <= 3,则最终总能找到一种以上计算顺序使得计算过程在【0,3】之间。

因为,假如此时先有了 + 1,无论 - 1 和 -2 的数量如何,如果有 - 1,直接和 + 1抵消,假如没有,在 0 <= sum <= 3 的前提下,完全可以再拿一个 + 2 出来,再用 - 2 进行抵消。(先有了 + 2 也同理,因为 ± 1 和 ± 2 总能不超出【0,3】的范围)

那么我们可以发现是 ± 3 的存在使得计算过程可能不合法。所以现在问题就转变成了:
① sum < 0 || sum > 3 输出“No”
②0 <= sum <= 3,尽可能合法得消除 ± 3,如果( + 3 的数量超过 1 || - 3 的数量超过 0 ),那么输出“No”,反之输出“Yes”。

因为:当 sum == 3 时,允许有一个 + 3,但是不能有 - 3。比如说 sum == 1,但是只剩下 2 2 -3 时,是不可能存在一种正确的操作顺序使得该过程总和不超过3且不小于0的。

代码

#include<bits/stdc++.h>

int main(){
    
    
	int t;
	scanf("%d", &t);
	while (t --) {
    
    
		int n;
		scanf("%d", &n);
		int a = 0, b = 0, c = 0, x = 0, y = 0, z = 0;
		while (n --) {
    
    
			int k;
			scanf("%d", &k);
			// 计算各个数的数量,0 可略去
			if (k == -1) ++ a;
			else if (k == -2) ++ b;
			else if (k == -3) ++ c;
			else if (k == 1) ++ x;
			else if (k == 2) ++ y;
			else if (k == 3) ++ z;
		}
		long long sum = x + 2 * y + 3 * z - a - 2 * b - 3 * c;
		if (sum < 0 || sum > 3) {
    
    
			printf("No\n");
			continue;
		}
		while (z && c) -- z, -- c;  // +3 -3
		while (z && a && b) -- z, -- a, -- b;  //+3 -1 -2
		while (z && x && b >= 2) -- z, -- x, b -= 2;  // +3 -2 +1 -2
		while (z && a >= 3) -- z, a -= 3;    //+3 -1 -1 -1
		while (c && x && y) -- c, --x, --y;  // +1 +2 -3
		while (c && a && y >= 2) -- c, -- a, y -= 2;  // +2 -1 +2 -3
  		while (c && x >= 3) -- c, x -= 3;   //+1 +1 +1 -3
		
		if (z > 1 || c) printf("No\n");
		else printf("Yes\n");
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/CSDNWudanna/article/details/112505148