2023秋招笔试题记录-自用

小美的01串翻转(美团0819)

小美定义一个 01 串的权值为:每次操作选择一位取反,使得相邻字符都不相等的最小操作次数。

例如,"10001"的权值是 1,因为只需要修改一次:对第三个字符取反即可。

现在小美拿到了一个 01 串,她希望你求出所有非空连续子串的权值之和,你能帮帮她吗?

输入描述

一个仅包含’0’和’1’的字符串,长度不超过 2000。

输出描述

所有非空子串的权值和。

示例1

输入

10001

输出

8

说明

长度为 2 的子串中,有 2 个"00"的权值是 1。

长度为 3 的 3 个子串权值都是 1。

长度为 4 的 2 个子串权值都是 1。

长度为 5 的 1 个子串权值是 1。

总权值之和为 2+3+2+1=8

思路与代码

前缀和模拟。

不难发现,最后的序列必然是 10101…或者是 010101…,因此对于长度为n的序列,最后的结果只有2种可能。枚举其中的最小值即可。

pres1和pres2分别表示两种可能的修改次数的前缀和。
枚举所有可能的子串,使用前缀和快速求得区间的修改次数(权值),叠加即可。

s = [int(c) for c in input()]
n = len(s)

# 枚举第一位就好了
s1 = list(s)
s2 = list(s)

s2[0] ^= 1

for i in range(1,n):
    s1[i] = s1[i-1] ^ 1
    s2[i] = s2[i-1] ^ 1

pres1, pres2 = [0]*(n+1),[0]*(n+1)

for i in range(1, n+1):
    pres1[i], pres2[i] = pres1[i-1], pres2[i-1]
    //需要改动,则次数+1
    if s[i-1] != s1[i-1]: pres1[i] += 1
    if s[i-1] != s2[i-1]: pres2[i] += 1


res = 0
//将不同情况字串的结果相加
for i in range(n):
    for j in range(i+1,n):
       res += min(pres1[j+1]-pres1[i], pres2[j+1]-pres2[i])

print(res)

小红的皇后(JD)

来自 https://mp.weixin.qq.com/s/E9vgnAASzcuojHq8DDGasw
有一个n行m列的棋盘,有一些格子是障碍物不能通过。小红控制一个皇后在从左上角出发,每次移动她可以控制皇后进行以下三种方式中的一种:

1.向右移动若干个格子

2.向下移动若干个格子

3.向右下移动若干个格子。

用数学语言描述,当前的坐标在(x,y)时,每次移动可以到(x+k,y)或(x,y+k)或(x+k,y+k)其中k为任意正整数。移动的前提是,路径上没有障碍物。

小红想知道,皇后从左上角移动到右下角,最少要移动多少步?

输入描述

第一行输入两个正整数n和n,代表行数和列数。
接下来的n行,每行输入一个长度m的字符串,用来表示棋盘。
其中’.‘代表可以通过的位置,'*'代表障碍物。
保证左上角和右下角都不是障碍物。
1<=n,m<=2000。

输出描述

如果无法到达,请输出-1。

否则输出一个整数,代表最少的移动次数。

思路与代码:

记录到达每个点的步数,向三个方向走的时候每次尽可能走到最远;在遍历三个方向的过程中,首先需要判断一下当前节点和对应方向的上一个节点是否步数相同,如果相同,则说明可以一步到达,则没必要再遍历这个方向上的点,跳过此方向;否则则说明改变了前进方向,在当前节点的步数基础上+1才可到达下一个节点。

#include
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<char>> grid(n,vector<char>(m));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>grid[i][j];
        }
    }
    int dx[3]={1,0,1};
    int dy[3]={0,1,1};
    vector<vector<int>> distance(n,vector<int>(m,INT_MAX));
    distance[0][0]=0;
    queue<pair<int,int>> q;
    q.push({0,0});
    while(!q.empty()){
        auto current = q.front();
        q.pop();
        int x = current.first;
        int y = current.second;
        for(int i=0;i<3;i++){
        //三种方向的试探
            int oldX = x-dx[i];
            int oldY = y-dy[i];
            if(oldX>=0&&oldX<n&&oldY>=0&&oldY<m){
                if(distance[oldX][oldY]==distance[x][y]){
                    continue;
                }
            }
            int index=1;
            do{
                int newX = x+dx[i]*index;
                int newY = y+dy[i]*index;
                index++;
                if(newX<0||newY<0||newX>=n||newY>=m||grid[newX][newY]=='*'){
                    break;
                }
                if(distance[newX][newY]>distance[x][y]+1){
                //如果原先这个格子已经走过了,那么就将他更新为最近的最小值
                    distance[newX][newY] = distance[x][y]+1;
                    q.push({newX,newY});
                }
            }while(true);
        }
    }
    if(distance[n-1][m-1]==INT_MAX){
        cout<<-1<<endl;
    }
    else{
        cout<<distance[n-1][m-1]<<endl;
    }
    return 0;
}

小红吃药(JD 0819)

参考京东秋招0819-https://ujimatsu-chiya.github.io
小红准备买药治病。已知共有n种症状和m种药,第i种药可以治疗一些症状,但可能会导致一些副作用,添加一些新的症状。小红依次服用了一些药,请你告诉小红,当她每次服用一副药时,当前还有多少症状?

输入描述

第一行输入一个正整数n,代表症状的数量
第二行输入一个长应为n的01串,第i位是‘1’代表小红目前有第i个症状,第i位是‘0’代表没有该症状。
第三行输入一个正整数m,代表药的数量
接下来的2 * m行,每2行描述一副药:
第一行输入一个长度为n的01串,代表该药能治疗的症状。’1‘代表可以治疗,‘0’代表不能治疗。
第二行输入一个长度为n的01串,代表该药会产生的副作用。’1‘代表会产生该症状,’0‘代表不会产生。
接下来的一行,输入一个正整数q,代表小红服用的药数量。
接下来的q行,每行输入一个正整数u,代表小红服用了第u副药。
1<=n<=20
1<=m,q<=10^4
1<=ai,u<=m
保证每副药的副作用产生的症状和该药治疗的症状是不会重复的,即不会存在同一个位置的两个01串都是‘1’。
输出描述

输出q行,每行输入一个正整数,代表当前小红服用药后,身体有多少症状。

示例1

4
0101
3
1100
0010
0101
1000
1001
0000
3
2
3
1
输出

1
0
1

2023秋招笔试题记录-自用_第1张图片

# include 
using namespace std;

const int N=10004;
int m1[N],m2[N],n,q,m;
int parse(string &s){
    int x=0;
    for(int i=0;i<n;i++)
        x|=int(s[i]-'0')<<i;
    return x;
}
int main(){
    string s,t;
    cin>>n>>s>>m;
    int st=parse(s);
    for(int i=1;i<=m;i++){
        cin>>s>>t;
        m1[i]=parse(s);
        m2[i]=parse(t);
    }
    cin>>q;
    int id;
    while(q--){
        cin>>id;
        //((1<
        //m1[i]是第i种药的治疗效果,与全1序列异或的结果是取反。最终将关键位置原本为1,先取反就变为了0,但非关键位全为1
        //将((1<
        st &= ((1<<n)-1)^m1[id];
        //st |= m2[id];就是添加副作用病症
        st |= m2[id];
        cout<<__builtin_popcount(st)<<"\n";
    }
}

分割序列(联想 0809)

来自:https://mp.weixin.qq.com/s/5-A_-HcahKScXnMopcVKyg

题目描述:

定义f(A)表示将序列A进行unique操作之后的序列的元素个数。unique 操作是指将相邻且相同的元素合成一个元素,再按照原序列的相对顺序进行排列之后得到的序列。例如,[1,1,1,2,2,3,3,1] 进行 unique 操作之后的序列为[1,2,3,1]; [1,2,3,3,2,1] 进行unique操作之后的序列为[1,2,3,2,1]; [1,1,1,1,1] 进行unique操作之后得到的序列为 [1]。

现在,输入一个长度为n的序列S,你需要将其划分为k段,使得每一段都不为空,且你需要最大化所有段的f函数的值之和。你只需要输出这个最大值就行。

输入描述

第一行两个正整数n,k(1<=k<=n<=10^5);

第二行n个由空格隔开的正整数a1,a2,…,an(1<=ai<=10000),表示输入的序列S.

输出描述

输出一个整数,表示所求的最大值。

样例输入

8 3
1 1 1 2 2 3 3 1
样例输出

6

思路与代码

观察得出以下规律:

  • 分割一次,肯定会让最后的结果变大
  • 如果切割一次,应该选择相邻且不同的位置切割,这操作可以使得f函数的值增大1。

因此,可以先算出在不切割的时候的f函数的值之和 res,最多可以切割k-1次,也就是最多可以使得res增大k-1次,只要有k-1个切割点满足相邻且不同。

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();

        /**
         * 1 1 1 2 2 3 3 1
         *
         * */

        int[] a = new int[n];
        int samecnt = 0;
        int res = 0;
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();
            if (i > 0 && a[i] == a[i-1]) samecnt++;
            if (i == 0 || a[i] != a[i-1]) res++;
        }
//两种情况,取较小值
//可以来切分的相同序列长度  为较小值,就把他全切完
//可以来切分的相同序列长度  为较大值,那就把k-1次切割机会用完
        System.out.println(res + Math.min(samecnt, k-1));

    }

}

你可能感兴趣的:(机试题目练习,linux,c++,网络,求职)