• 记录一些面试题


    携程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;
    }
    
  • 相关阅读:
    Git入门
    基于sendmail的简单zabbix邮件报警
    zabbix agentd安装
    我整理的一份来自于线上的Nginx配置(Nginx.conf),希望对学习Nginx的有帮助
    【转载】Spring Boot引起的“堆外内存泄漏”排查及经验总结
    lodop+art-template实现web端漂亮的小票样式打印
    《阿里巴巴Java开发手册》改名《Java开发手册》,涵盖史无前例的三大升级
    Spring Boot的学习之路(02):和你一起阅读Spring Boot官网
    Spring Boot的学习之路(01):缘起
    『 效率工具 』Spring Boot版的轻量级代码生成器,减少70%以上的开发任务
  • 原文地址:https://www.cnblogs.com/shinianhuanniyijuhaojiubujian/p/16146706.html
Copyright © 2020-2023  润新知