蚂蚁C++后端暑期实习4.11 笔试题:
1. 签到题:给一个数组,找有多少个出现数量是素数的素数。
2. 给一个n*m的网格(n,m <= 1e9),在每一个点你可以往左上,左下,右上,右下走,当遇到四个顶点时会原路反弹,在遇到不是顶点的边界时90度反弹,类似一个反射面,给定初始位置和出发方向,问走多少步回到起点。
Sample Input 1 5 7 1 7 DL Sample Output 1 24 Sample Input 2 3 3 1 2 DR Sample Output 2 4
3. 给一个01串,长度2e5,现在需要构造一个等长度的小写字母的字符串,要求所有相同字母对应位置的01串的数都同为0或同为1,比如01串为010,字符串可以是aba,abc,但不能是aaa,因为a对应了0和1。让你构造一个这样的字符串,使得最多数量的字母最少。
Sample Input 01010101010101010101010101 Sample Output anbocpdqerfsgthuivjwkxlymz
题解:
1. 签到
2. 把边界当成镜子,然后遇到边界不会反射,而是继续直走,相当于把原来的矩形翻转180度,这样当走到经过一系列翻转后的起点就相当于回到起点,构造二元一次方程使用扩展欧几里得算法求解即可。由于答案可以超过int范围,暴力很容易超时。
#include <bits/stdc++.h> using namespace std; int n, m, x, y, start_x, start_y; char s[3]; int exgcd(int a,int b,int &x,int &y){ if(!b){ x=1; y=0; return a; } int d = exgcd(b, a % b, y, x); y = y - a / b * x; return d; } //求解方程ax + by = c的最小正整数解 int solve(int a, int b, int c) { int gcd = exgcd(a, b, x, y); if(c % gcd != 0) return -1; x = x * (c / gcd); x = x % (abs(b / gcd)); if(x <= 0) x = x + abs(b / gcd); return x; } int main() { scanf("%d %d %d %d %s", &n, &m, &start_x, &start_y, s); //通过把图翻转将不同方向统一到DR方向处理 if(s[0] == 'U' && s[1] == 'R') { start_x = n - start_x + 1; }else if(s[0] == 'U' && s[1] == 'L') { start_x = n - start_x + 1; start_y = m - start_y + 1; }else if(s[0] == 'D' && s[1] == 'L') { start_y = m - start_y + 1; } long long ans = 2e18; //printf("%d %d\n", start_x, start_y); //四种类型的点进行分别处理 int a = 2 * (n - 1), b = -2 * (m - 1), c = 0; int p = solve(a, b, c); if(p != -1) ans = min(ans, 2LL * p * (n - 1)); a = 2 * (n - 1), b = -2 * (m - 1), c = -2 * start_y + 2; p = solve(a, b, c); if(p != -1) ans = min(ans, 2LL * p * (n - 1)); a = 2 * (n - 1), b = -2 * (m - 1), c = 2 * start_x - 2; p = solve(a, b, c); if(p != -1) ans = min(ans, 2LL * p * (n - 1) - 2LL * start_x + 2); a = 2 * (n - 1), b = -2 * (m - 1), c = 2 * start_x - 2 * start_y; p = solve(a, b, c); if(p != -1) ans = min(ans, 2LL * p * (n - 1) - 2 * start_x + 2); printf("%lld\n", ans == 2e18 ? -1 : ans); }
3. 可以从小到大枚举这个最高的高度,对字母a - z依次填充,尽量按照枚举的高度填,并且使得0的字母的数量总和为01串的0的数量,1的为1的,如果可以,那更大的高度也一定可以,所以符合二分法,但没有必要,直接暴力搜就完事了。
#include <bits/stdc++.h> using namespace std; char s[200005]; int cnt01[2] = {0}, alpha[26], mark[26]; queue<char> avail[2]; int main() { scanf("%s", s); int n = strlen(s), ans = 1e9; for(int i = 0; i < n; i++) cnt01[(int)(s[i] - '0')]++; for(int max_height = 1; max_height <= n; max_height++) { int sum0 = 0, sum1 = 0; for(int i = 0; i < 26; i++) { if(sum0 < cnt01[0]) { mark[i] = 0; alpha[i] = min(cnt01[0] - sum0, max_height); sum0 += min(cnt01[0] - sum0, max_height); }else{ mark[i] = 1; alpha[i] = min(cnt01[1] - sum1, max_height); sum1 += min(cnt01[1] - sum1, max_height); } } if(sum0 == cnt01[0] && sum1 == cnt01[1]) break; } for(int i = 0; i < 26; i++) { for(int j = 0; j < alpha[i]; j++) { avail[mark[i]].push((char)('a' + i)); } } for(int i = 0; i < n; i++) { printf("%c", avail[(int)(s[i] - '0')].front()); avail[(int)(s[i] - '0')].pop(); } printf("\n"); }#软件开发2023笔面经#