记录一些面试题
携程2022.04.14
一个数组,一些数是'R',一些是'B', 取两个不同颜色的数,且数值相等,多少种取法
输入
5
1 2 1 2 2
BRRBB
输出
3
把蓝色数每个数值的个数用map存起来,遍历红色,把对应的数值个数取出来累加即可,int会炸
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int main(){
string s;
ll n, ans = 0;
map<int, int> mp;
cin >> n;
int a[n + 1];
for(int i = 0; i < n; i++)
cin >> a[i];
cin >> s;
for(int i = 0; i < n; i++)
if(s[i] == 'B') mp[a[i]] ++;
for(int i = 0; i < n; i++)
if(s[i] == 'R') ans += mp[a[i]];
cout << ans << endl;
return 0;
}
题目大意:一个数字串。从中选取一个子序列使其实9的倍数,有多少种方案,允许有前导0,对1000000007取模
输入
1188
输出
5
输入
0123
输出
1
位数值之和是9的倍数则序列是9的倍数,遍历数串,当前值可取可不取,若取则会对前面的值产生影响,若当前数值为x,则依次加0~9之后会对应更新,不取的话就是本身,则新的值就是dp[i][j + x] = dp[i - 1][j] + dp[i - 1][j + x],用滚动数组维护就行。
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int main(){
string s;
cin >> s;
ll vis[2][10], flag = 0; // 维护9个数字的个数
memset(vis, 0, sizeof(vis));
for(int i = 0, x; i < s.size(); i++){
x = s[i] - '0';
x %= 9;
for(int j = 0; j < 9; j++){
int k = (j + x) % 9;
vis[flag][k] = (vis[flag ^ 1][j] + vis[flag ^ 1][k]) % mod; // 当前数字取 or 不取
}
vis[flag][x] = (vis[flag][x] + 1) % mod; //单独作为一个序列
// for(int j = 0; j < 9; j++)
// cout << vis[flag][j] << " ";
// cout << endl;
flag ^= 1; // 更新状态,用两个数组交替维护
}
cout << vis[flag ^ 1][0] << endl; // 最后所有数字取完之后,0对应的数字就是方案的数目
return 0;
}
给定一个01字符串, 如10101010110,每次可以交换相邻的字符使得01交替出现。确保字符合法。
11100
3
这里给一个用归并排序实现的
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int a[200005], b[200005], ans = 0;
void MergeArrays(int *a, int left, int mid, int right, int *b)
{
int l = left,r = mid + 1,t = left;
while(l <= mid && r <= right){
if(a[l] < a[r]){
ans += max(0, l - t);
b[t] = a[l];
l++; t++;
}else{
ans += max(0, r - t);
b[t] = a[r];
r++; t++;
}
}
while(l <= mid){
ans += max(0, l - t);
b[t++]=a[l++];
}
while(r <= right){
ans += max(0, r - t);
b[t++] = a[r++];
}
t = left;
while(left<=right) a[left++]=b[t++];
}
void MergeSort(int *a, int left, int right, int *b)
{
if(left >= right) return ;
int mid = left + right>>1;
MergeSort(a, left, mid, b);
MergeSort(a, mid+1, right, b);
MergeArrays(a, left, mid, right, b);
}
void dfs(string s, char ch){
int n = s.size(), l = 0, r = 1;
for(int i = 0; i < n; i++){
if(s[i] == ch){
a[i] = l;
l += 2;
}else{
a[i] = r;
r += 2;
}
}
MergeSort(a, 0, n - 1, b);
}
int main(){
string s;
cin >> s;
int o1 = 0, o0 = 0, inf;
for(int i = 0; i < s.size(); i++)
s[i] == '1' ? o1 ++ : o0 ++;
if(o1 > o0){
dfs(s,'1');
inf = ans;
}
else if(o1 < o0){
dfs(s,'0');
inf = ans;
}
else {
dfs(s, '1');
inf = ans;
ans = 0;
dfs(s, '0');
inf = min(inf, ans);
}
cout << inf <<endl;
return 0;
}
网易互娱
缓存命中,有一个LRU(最近最少使用)缓存的访问记录R,记录量为m,大小为n,计算缓存的命中次数
输入
[1, 2, 1, 3, 2], 2
输出
1
解释
初始缓存为[]
访问1,未命中,更新为[1]
访问2,未命中,更新为[2, 1]
访问1,命中,更新为[1, 2]
访问3,未命中,更新为[3, 1]
访问2,未命中,更新为[2, 3]
思路,对每次询问,做个累加记录,和上一次相同的不变,不同的继续累加,更新映射,查询时差值小于n-1在缓存中命中,不在则未命中
int main(){
map<int, int> a, b;
int x, n, m;
vector<int> v;
cin >> m;
for(int i = 0; i < m; i++){
cin >> x;
v.push_back(x);
}
cin >> n;
n--;
int ans = 0, cnt = 0;
for(int i = 0; i < v.size(); i++){
x = v[i];
if(a[x] == 0) a[x] = ++cnt;
else{
if((cnt - a[x]) <= n) ans++;
if(a[x] == cnt) continue;
else a[x] = ++cnt;
}
}
cout << ans << endl;
return 0;
}
一个n个结点m条边的无向图,每个结点权值已知[1, \(10^9\)], 定义一条边为权重为两个结点乘积末尾0的数量,删除一条边,可以获得这条边的价值, 保证图联通的情况下,最多可以获得多少价值
输入
\(n, m\) [1, \(10^5\)]
\(a_1, a_2, \dots a_n\)
\(u, v\)
\(\cdots\)
5 3
5 8 25
1 2
2 3
1 3
输出
2
求最小生成树,总权重减去最小生成树权重就是价值
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int a[100005][2], f[100005];
void solve(long long int x, int y){
int ans = 0;
while(x % 5 == 0){
a[y][0] ++;
x /= 5;
}
while(x % 2 == 0){
a[y][1] ++;
x /= 2;
}
}
int find(int x){
return f[x] = f[x] == x ? x : find(f[x]);
}
int main()
{
int n, m, inf = 0, ans = 0;
cin >> n >> m;
ll c;
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i++){
cin >> c;
solve(c, i);
}
vector<vector<int>> edge(m, vector<int>());
for(int i = 0, l, r,y; i < m; i++){
cin >> l >> r;
edge[i].push_back(l); edge[i].push_back(r);
y = min(a[l][0] + a[r][0], a[l][1] + a[r][1]);
edge[i].push_back(y);
inf += y;
}
sort(edge.begin(), edge.end(), [](vector<int>& o1, vector<int>& o2){
return o1[2] <= o2[2];
});
for(int i = 0; i <= n; i++)
f[i] = i;
for(int i = 0, x, y; i < m; i++){
x = find(edge[i][0]);
y = find(edge[i][1]);
if(x == y) continue;
ans += edge[i][2];
f[x] = y; n--;
if(n == 1) break;
}
cout << inf - ans << endl;
return 0;
}
定义一个区间的权值为区间内所有树的乘积末尾0的数量,求\(\sum_{i = 1}^n\sum_{j = 1}^nf(i,j)\)
输入
\(n\) [1, \(10^5\)]
\(a_1, a_2, \dots a_n\) [1, \(10^9\)]
3
10 2 5
输出
5
一个数若有x个末尾0,那么分解这个数以后必定至少有x个5,x个2
统计每个数2,5的数量
计算前缀和a[n], a[i]为[1-i]2的数量或者5的数量,因此,1被累加了n次,i被累加了n-i+1次
并把全部前缀和加到树桩数组
查询时,从左到右依次查询,每次查询过后就要将当前位置本身对应的2,5的数量从树桩数组中剪掉其剪掉的值为-1 * (n - i + 1) * x。
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int a[100005][4], dp[2][100005];
void solve(int x, int y){
int ans = 0;
while(x % 5 == 0){
a[y][0] ++;
x /= 5;
}
while(x % 2 == 0){
a[y][1] ++; x /= 2;
}
}
int lowbit(int x){
return (x & (-x));
}
void add(int x, int k, int val){
for(int i = x; i < 100005; i += lowbit(i))
dp[k][i] += val;
}
int query(int x, int k){
int ans = 0;
for(int i = x; i; i -= lowbit(i))
ans += dp[k][i];
return ans;
}
int main(){
int n, inf = 0;
cin >> n;
memset(a, 0, sizeof(a));
memset(dp, 0, sizeof(dp));
for(int i = 1, x; i <= n; i++){
cin >> x;
solve(x, i);
}
for(int i = 1; i <= n; i++){
a[i][2] = a[i][0];
a[i][3] = a[i][1];
a[i][0] += a[i - 1][0]; // 区间1-i的2, 5的总个数
a[i][1] += a[i - 1][1];
}
for(int i = 1; i <= n; i++){
add(i, 0, a[i][0]);
add(i, 1, a[i][1]);
}
for(int i = 1; i <= n; i++){
inf += min(query(n, 0), query(n, 1));
add(i, 0, -(a[i][2] * (n - i + 1))); // 剪掉所有包含当前i的区间
add(i, 1, -(a[i][3] * (n - i + 1)));
}
cout << inf << endl;
return 0;
}
有6个数组,每个数组里面有n个对(x, y)\([1 \le n \le 30][0 \le x \le 25][0 \le y \le 50]\), 要求每个数组中必须选出一个对, 使得\(sum_{i = 1}^6 x_i \ge 100\)的情况下\(sum_{i = 1}^6 y_i\)最大
输入
T组输入
n行
p, x, y \((1 \le p \le 6)\)
输出
没结果的话输出"TAT"
输入
2
12
1 12 6
1 14 10
2 22 3
2 3 38
3 24 1
3 3 15
3 11 23
4 13 2
5 19 10
5 17 11
5 16 2
6 20 2
11
1 14 16
1 17 3
2 6 32
3 3 24
4 12 3
4 13 0
5 22 5
5 21 4
6 3 37
6 14 6
6 23 0
输出
29
TAT
思路, 已知每个x不超过25,则选6个之后最大不超过150,且每组必须都有一个,因此可以确定上一轮选取了在选当前轮,因此可以转化为背包问题
dp[i][0] 代表从开始都该轮得到i分的情况下最大y和, 并标记dp[i][1] = 1;
每轮都有对被选上,数组可以滚动进行,因此可以开辟三维dp[2][155][2]
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long int
int main(){
int T, n, x, y, z;
cin >> T;
while(T--){
cin >> n;
vector<vector<pair<int, int>> > v(6, vector<pair<int, int>>());
for(int i = 0; i < n; i++){
cin >> x >> y >> z;
x --;
v[x].push_back(make_pair(y, z));
}
int dp[2][155][2], flag = 0, ok = 0, ans = 0; // 标记位是必须的,因为x,y可能为0
memset(dp, 0, sizeof(dp));
for(int i = 0; i < v[0].size(); i++){
dp[0][v[0][i].first][0] = max(dp[0][v[0][i].first][0], v[0][i].second);
dp[0][v[0][i].first][1] = 1;
}
for(int i = 1; i < 6; i++){
memset(dp[flag ^ 1], 0, sizeof(dp[flag ^ 1]));
for(int j = 0; j < v[i].size(); j++){
x = v[i][j].first, y = v[i][j].second;
for(int k = 25 * i; k >= 0; k--){
if(dp[flag][k][1] == 0) continue;
dp[flag ^ 1][k + x][0] = max(dp[flag ^ 1][k + x][0], dp[flag][k][0] + y);
dp[flag ^ 1][k + x][1] = 1;
}
}
flag ^= 1;
}
for(int i = 100; i <= 150; i++){
if(dp[flag][i][1] == 1){
ok = 1;
ans = max(ans, dp[flag][i][0]);
}
}
ok ? cout << ans << endl : cout << "TAT" << endl;
}
return 0;
}