腾讯2018春招技术类编程题

 

目录

1. 翻转数列

2. 纸牌游戏

3. 贪吃的小Q

4.小Q的歌单

5、安排机器

6、画家小Q


1. 翻转数列

小Q定义了一种数列称为翻转数列:
给定整数n和m, 满足n能被2m整除。对于一串连续递增整数数列1, 2, 3, 4..., 每隔m个符号翻转一次, 最初符号为'-';。
例如n = 8, m = 2, 数列就是: -1, -2, +3, +4, -5, -6, +7, +8.
而n = 4, m = 1, 数列就是: -1, +2, -3, + 4.
小Q现在希望你能帮他算算前n项和为多少。

 

输入描述:

输入包括两个整数n和m(2 <= n <= 109, 1 <= m), 并且满足n能被2m整除。

 

输出描述:

输出一个整数, 表示前n项和。

 

输入例子1:

8 2

 

输出例子1:

8

首先观察数列,我们可以将一组负正的数出现(如-1,-2,3,4)看做一组,则n个数一共有n/(2m)组,而每一组求和结果为m*m,于是得到前n项和公式为Sn = n*m*m/2m = m*n/2,实现代码如下

import java.util.*;
public class Main {
    public static void main(String args []) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();
        int m = sc.nextInt();
        System.out.println((n / (m << 1)) * (m * m));
    }
}

2. 纸牌游戏

牛牛和羊羊正在玩一个纸牌游戏。这个游戏一共有n张纸牌, 第i张纸牌上写着数字ai。
牛牛和羊羊轮流抽牌, 牛牛先抽, 每次抽牌他们可以从纸牌堆中任意选择一张抽出, 直到纸牌被抽完。
他们的得分等于他们抽到的纸牌数字总和。
现在假设牛牛和羊羊都采用最优策略, 请你计算出游戏结束后牛牛得分减去羊羊得分等于多少。

 

输入描述:

输入包括两行。
第一行包括一个正整数n(1 <= n <= 105),表示纸牌的数量。
第二行包括n个正整数ai(1 <= ai <= 109),表示每张纸牌上的数字。

 

输出描述:

输出一个整数, 表示游戏结束后牛牛得分减去羊羊得分等于多少。

 

输入例子1:

3
2 7 4

 

输出例子1:

5

题中所说的最优策略就是每次抽取剩下的纸牌数中的最大值,其实并不是随机的了。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner =new Scanner(System.in);
        int n=Integer.parseInt(scanner.nextLine());
        String[] a=scanner.nextLine().split(" ");
        int[] A =new int[a.length];
        for(int i=0;i<a.length;i++){
            A[i]=Integer.parseInt(a[i]);
        }
        Arrays.sort(A);
        int k=0,y=0;
        boolean b=true;
        for(int i=A.length-1;i>=0;i--){
            if(b){
                k+=A[i];
                b=false;
            }else {
                y+=A[i];
                b=true;
            }
        }
        System.out.println((k-y));
    }
}

3. 贪吃的小Q

小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力

 

输入描述:

每个输入包含一个测试用例。
每个测试用例的第一行包含两个正整数,表示父母出差的天数N(N<=50000)和巧克力的数量M(N<=M<=100000)。

 

输出描述:

输出一个数表示小Q第一天最多能吃多少块巧克力。

 

输入例子1:

3 7

 

输出例子1:

4

那么问题就在于从最大的一个一个往前找速度太慢了,时间复杂度高,就可以改为二分查找:

首先赋低位min为0,高位max为所有巧克力数n,

while(min<max),循环试探(min+max)/2向上取整个数个巧克力是否满足题意,

1:如果满足题意,那么再判断一下(min+max)/2向上取整+1个数个巧克力是否满足题意:

     (1)如果不满足题意,那么就说明当前的(min+max)/2向上取整个巧克力就是要找的最大的

      (2)如果满足题意,那么就说明当前的(min+max)/2向上取整个巧克力取小了,赋低位min=(m       in+max)/2向上取整,继续while循环试探

2:如果不满足题意,那么说明当前的(min+max)/2向上取整个巧克力取大了,赋高位max=(min+max)/2向上取整,继续while循环试探

public static void main1(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();//天数
        int m = sc.nextInt();//巧克力个数
        double min = 0;//设置二分初始低位为0
        double max = (double) m;//设置二分初始高位为初始巧克力个数m
        double stillHas = (double) m;//剩余巧克力个数
        boolean flag = true;
        double temp;
        while (min < max){//当低位<高位时,开始二分查找
            temp = Math.ceil((min + max) / 2);//取低位和高位中间的一位向上取整,即为试探的第一天吃的巧克力数
            for (int i = 1; i < n + 1; i++) {//循环n天
                if (temp <= stillHas) {//当前天需要吃的巧克力个数<=剩余巧克力个数时,减少巧克力,同时第二天巧克力个数变为第一天的一半
                    stillHas = stillHas - temp;
                    temp = Math.ceil(temp / 2);
                } else {//当前天需要吃的巧克力个数>剩余巧克力个数时,说明没有撑到n天巧克力吃完,置flag=false;跳出循环
                    flag = false;
                    break;
                }
            }
            if (flag) {//flag==true,说明上面的for循环正常循环结束跳出,说明当前的第一天吃的Math.ceil((min+max)/2)个巧克力满足要求
                //判断一下,如果比Math.ceil((min+max)/2)个巧克力大1个巧克力时
                //isTrue返回false说明再大1个巧克力就不满足要求了,那么当前的Math.ceil((min+max)/2)就是最大的第一天吃的巧克力数,输出即可
                if (!isTrue(n, m, Math.ceil((min + max) / 2) + 1)) {
                    System.out.println((int) Math.ceil((min + max) / 2));
                    return;
                }//如果大1个巧克力仍然满足要求那么说明当前的第一天吃的Math.ceil((min+max)/2)取小了应取大一点的巧克力数,需要继续二分查找
                else {
                    min = Math.ceil((min + max) / 2);//取低位为当前的试探的第一天吃的巧克力数
                    stillHas = (double) m;//重置剩余巧克力数为总数
                }
            }//flag==false,说明上面的for循环遇到break跳出,说明当前的第一天吃的Math.ceil((min+max)/2)取大了应取小一点的巧克力数,需要继续二分查找
            else {
                max = Math.ceil((min + max) / 2);//取高位为当前的试探的第一天吃的巧克力数
                stillHas = (double) m;//重置剩余巧克力数为总数
                flag = true;//重置标志位
            }
        }
    }
    //用于判断当每天吃X个巧克力时是否能撑到父母回来的静态方法
    private static boolean isTrue(int n, double m, double x) {
        for (int i = 1; i < n + 1; i++) {
            if (x <= m) {
                m = m - x;
                x = Math.ceil(x / 2);
            } else {
                return false;
            }
        }
        return true;
    }

//二分查找,修改大佬们的边界,直接返回大于目标值的第一个下标 

下面这个别人写的,代码看着很简单,但是我还不太懂

import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        System.out.println(process(n, m));
    }
 
    public static int process(int n, int m){
        int l = 1;
        int r = m;
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(sum(mid, n) > m){
                r = mid - 1;
            }else{
                l = mid + 1;
            }
        }
        return l - 1;
    }

    public static int sum(int s, int n){
        int sum = 0;
        for(int i = 0; i < n; i++){
            sum += s;
            s = (s + 1) / 2;
        }
        return sum;
    }
}

4.小Q的歌单

小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌,现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次,在不考虑歌单内歌曲的先后顺序的情况下,请问有多少种组成歌单的方法。

输入描述:

每个输入包含一个测试用例。
每个测试用例的第一行包含一个整数,表示歌单的总长度K(1<=K<=1000)。
接下来的一行包含四个正整数,分别表示歌的第一种长度A(A<=10)和数量X(X<=100)以及歌的第二种长度B(B<=10)和数量Y(Y<=100)。保证A不等于B。

输出描述:

输出一个整数,表示组成歌单的方法取模。因为答案可能会很大,输出对1000000007取模的结果。

输入例子1:

5
2 3 3 3

输出例子1:

9

动态规划,模仿背包问题,问题简化为有x+y种物品,其中x种的容积为a,y种的容积为b,背包容积为k,问背包装满一共有多少种解法?

根据题意,只需要用到一维数组即可,代码简单,解法如下:

import java.util.*;
 
public class Main{
    public static final int ASD = 1000000007;
     
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int k=sc.nextInt();
        int a=sc.nextInt(), x=sc.nextInt();
        int b=sc.nextInt(), y=sc.nextInt();
        int[] dp = new int[k+1];
        dp[0] = 1;
        for(int i=0; i<x ; i++){
            for(int j=k; j>=a; j--){
                dp[j] = (dp[j] + dp[j-a]) % ASD;
            }
        }
         
        for(int i=0; i<y ; i++){
            for(int j=k; j>=b; j--){
                dp[j] = (dp[j] + dp[j-b]) % ASD;
            }
        }
         
        System.out.println(dp[k]);
        sc.close();
    }
}

5、安排机器

小Q的公司最近接到m个任务, 第i个任务需要xi的时间去完成, 难度等级为yi。
小Q拥有n台机器, 每台机器最长工作时间zi, 机器等级wi。
对于一个任务,它只能交由一台机器来完成, 如果安排给它的机器的最长工作时间小于任务需要的时间, 则不能完成,如果完成这个任务将获得200 * xi + 3 * yi收益。

对于一台机器,它一天只能完成一个任务, 如果它的机器等级小于安排给它的任务难度等级, 则不能完成。

小Q想在今天尽可能的去完成任务, 即完成的任务数量最大。如果有多种安排方案,小Q还想找到收益最大的那个方案。小Q需要你来帮助他计算一下。

 

输入描述:

输入包括N + M + 1行,
输入的第一行为两个正整数n和m(1 <= n, m <= 100000), 表示机器的数量和任务的数量。
接下来n行,每行两个整数zi和wi(0 < zi < 1000, 0 <= wi <= 100), 表示每台机器的最大工作时间和机器等级。
接下来的m行,每行两个整数xi和yi(0 < xi < 1000, 0 <= yi<= 100), 表示每个任务需要的完成时间和任务的难度等级。

 

输出描述:

输出两个整数, 分别表示最大能完成的任务数量和获取的收益。

 

输入例子1:

1 2
100 3
100 2
100 1

 

输出例子1:

1 20006

/**

 * 腾讯校招题:这里唯一的一个点就是时长少,但是难度等级大,而且收益会更高,这样就会破坏贪心的规则,只能借助更棒的算法来思考

 * @author quanhangbo

 *算法思路:根据题意,要完成一个任务必须满足机器的时间长大于任务任务长并且机器等级大于任务等级

 1.在这里我们用2个(机器,任务)二维数组来存储:比如说用mech[i][0]表示机器时间,mech[i][1]表示的是机器的难度系数

 2.存储后对对两个二维数组进行降序排序,先按时间长来降序排列,如果时间相同的话,按照等级排序

 3.遍历机器数,把满足条件的数存储到一个专门的数组中,由于是按照降序排列的话,数组前面的利益肯定是大于后面的

 4.遍历任务数,进行求和

 */

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        int m=in.nextInt();
        Node [] machine=new Node[n];
        Node [] task=new Node[m];

        for (int i=0;i<n;i++){
            Node node=new Node();
            node.setTime(in.nextInt());
            node.setLevel(in.nextInt());
            machine[i]=node;
        }
        for (int i=0;i<m;i++){
            Node node=new Node();
            node.setTime(in.nextInt());
            node.setLevel(in.nextInt());
            task[i]=node;
        }
        // 从收益公式来看,任务的执行时间越长、等级越高,则收益更大
        // 将任务、机器按时间从大到小排序,时间相同时,则按照等级从大到小排序
        Arrays.sort(task, new Comparator<Node>() { 
            @Override public int compare(Node o1, Node o2) {
            if (o1.getTime()==o2.getTime())
                return o2.getLevel().compareTo(o1.getLevel());
            return o2.getTime().compareTo(o1.getTime());
        }
        });
        Arrays.sort(machine, new Comparator<Node>() {
            @Override public int compare(Node o1, Node o2) {
            if (o1.getTime()==o2.getTime())
                return o2.getLevel().compareTo(o1.getLevel());
            return o2.getTime().compareTo(o1.getTime());
        }
        });
        int [] cont=new int[105]; //下标表示等级,等级范围是0-100
        int j=0;
        int num=0;
        long ans=0;
        for (int i=0;i<m;i++){
            //对每个任务进行遍历
            Node taski=task[i];
            //能执行该任务的各个等级的机器的数目
            while(j<n&& machine[j].getTime()>=taski.getTime()){
                cont[machine[j].getLevel()]++; //对任务i,能完成该任务的某等级的机器数+1
                j++;
            }
            for (int level=taski.getLevel();level<=100;level++){
                //尽量用最小的等级机器来执行
                if (cont[level]>0){
                    num++;
                    cont[level]--; //一个机器只能执行一个任务
                    ans+=(200*taski.getTime()+3*taski.getLevel());
                    break;
                }
            }
        }
        System.out.println(num+" "+ans);
    }

    static class Node{
        Integer time,level;

        public Integer getTime() {
            return time;
        }

        public void setTime(Integer time) {
            this.time = time;
        }

        public Integer getLevel() {
            return level;
        }

        public void setLevel(Integer level) {
            this.level = level;
        }
    }
}

6、画家小Q

画家小Q又开始他的艺术创作。小Q拿出了一块有NxM像素格的画板, 画板初始状态是空白的,用'X'表示。
小Q有他独特的绘画技巧,每次小Q会选择一条斜线, 如果斜线的方向形如'/',即斜率为1,小Q会选择这条斜线中的一段格子,都涂画为蓝色,用'B'表示;如果对角线的方向形如'\',即斜率为-1,小Q会选择这条斜线中的一段格子,都涂画为黄色,用'Y'表示。
如果一个格子既被蓝色涂画过又被黄色涂画过,那么这个格子就会变成绿色,用'G'表示。
小Q已经有想画出的作品的样子, 请你帮他计算一下他最少需要多少次操作完成这幅画。

输入描述:

每个输入包含一个测试用例。
每个测试用例的第一行包含两个正整数N和M(1 <= N, M <= 50), 表示画板的长宽。
接下来的N行包含N个长度为M的字符串, 其中包含字符'B','Y','G','X',分别表示蓝色,黄色,绿色,空白。整个表示小Q要完成的作品。

 

输出描述:

输出一个正整数, 表示小Q最少需要多少次操作完成绘画。

 

输入例子1:

4 4
YXXB
XYGX
XBYY
BXXY

 

输出例子1:

3

 

例子说明1:

XXXX
XXXX
XXXX
XXXX
->
YXXX
XYXX
XXYX
XXXY
->
YXXB
XYBX
XBYX
BXXY
->
YXXB
XYGX
XBYY
BXXY
import java.util.Scanner;
 
public class Main {
 
    public static void main(String[] args) {
 
        Scanner scanner = new Scanner(System.in);
        String tem = scanner.nextLine();
        int n = Integer.valueOf(tem.split(" ")[0]);
        int m = Integer.valueOf(tem.split(" ")[1]);
        char color[][] = new char[n][m];
        for (int i = 0; i < n; i++) {
            tem = scanner.nextLine();
            for (int j = 0; j < tem.length(); j++) {
                color[i][j] = tem.charAt(j);
            }
        }
        // 获取最小步数
        getMinStep(n, m, color);
        scanner.close();
    }
    /**
     * 获取最小步数 每画一个线直到颜色不同为止
     *
     * @param n
     * @param m
     * @param color
     */
    private static void getMinStep(int n, int m, char color[][]) {
 
        int step = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (color[i][j] == 'Y') {
                    dfs_y(i, j, n, m, color); // 画y
                    step++;
                } else if (color[i][j] == 'B') {
                    dfs_b(i, j, n, m, color); // 画B
                    step++;
                } else if (color[i][j] == 'G') {
                    dfs_y(i, j, n, m, color); // 先画y
                    step++;
                    dfs_b(i, j, n, m, color); // 在画B
                    step++;
                }
            }
        }
        System.out.println(step);
    }
    /**
     * 当前位置画y,是否在后续位置继续画y
     *
     * @param x
     * @param y
     */
    private static void dfs_y(int x, int y, int n, int m, char color[][]) {
        // 根据要求决定当前位置是否能画y
        if (x >= 0 && x < n && y >= 0 && y < m && (color[x][y] == 'Y' || color[x][y] == 'G')) {
            if (color[x][y] == 'G') {
                color[x][y] = 'B'; // 如果当前位置要求画的是G,那么画了Y之后下一次只能画B
            } else {
                color[x][y] = 'X'; // 如果当前位置要求画的是Y,那么画了Y之后下一次不需要再画
            }
 
            // 是否连笔继续画,Y对应的是画\,即左上或者右下
            dfs_y(x - 1, y - 1, n, m, color);
            dfs_y(x + 1, y + 1, n, m, color);
 
        }
 
    }
    /**
     * 当前位置画B,是否在后续位置继续画B
     *
     * @param x
     * @param y
     */
    private static void dfs_b(int x, int y, int n, int m, char color[][]) {
        // 根据要求决定当前位置是否能画B
        if (x >= 0 && x < n && y >= 0 && y < m && (color[x][y] == 'B' || color[x][y] == 'G')) {
            if (color[x][y] == 'G') {
                color[x][y] = 'Y'; // 如果当前位置要求画的是G,那么画了Y之后下一次只能画Y
            } else {
                color[x][y] = 'X'; // 如果当前位置要求画的是B,那么画了B之后下一次不需要再画
            }
            // 是否连笔继续画,B对应的是画/,即左下或者右上
            dfs_b(x + 1, y - 1, n, m, color);
            dfs_b(x - 1, y + 1, n, m, color);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值