腾讯暑期实习算法岗笔试题(java) 2020.04.26场

腾讯2020暑期实习在线笔试 4.26场


C代码题解在这里

T1

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import java.util.Scanner;
public class Main {
    public static void main(String[] args){
        // 思路:只杀满足wi>ci/m的怪物,性价比高
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int allC = 0; // 总支出(血量)
        int allW = 0; // 总收益(金币):1 金币= m 血量
        while(n>0){
            int c = sc.nextInt();
            int w = sc.nextInt();
            if(w*m > c){ // 只有当第i个怪兽的收益血量大于支出血量,才选择打
                allW += w;
                allC += c;
            }
            n--;
        }
        int pure = allC%m == 0? (allW - allC/m) : (allW - allC/m - 1);
        System.out.println(pure);
    }
}

T2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        // 思路:见示意图
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        double a = sc.nextDouble();
        double b = sc.nextDouble();
        double c = sc.nextDouble();
        double delta = 4 * a * a - 8 * a * b * c;
        if(delta <= 0){
            System.out.println("0");
            return;
        }
        double y1 = (2*a - Math.sqrt(delta)) / (2*b);
        double y2 = (2*a + Math.sqrt(delta)) / (2*b);
        double s = (a - b*c) / (b*b) * (y2 - y1) - (y2 * y2 * y2 - y1*y1*y1) / (6*a);
        System.out.printf("%.10f\n",s);
    }
}

T3

在这里插入图片描述
在这里插入图片描述
算法思路:
直接考虑冲突比较难,可以用容斥原理:总方案 - 不冲突的方案。
总方案:每个犯人都是 m m m种编号方案,因此共 n m n^{m} nm种。
不冲突的方案:第一个犯人有 m m m种编号方案,之后每一个犯人都与前一个不同因此为 m − 1 m-1 m1种编号方案。共 m ∗ ( m − 1 ) n − 1 m*(m-1)^{n-1} m(m1)n1种。
所以答案就是两者做差,为 n m − m ∗ ( m − 1 ) n − 1 n^{m} - m*(m-1)^{n-1} nmm(m1)n1种。
用快速幂+取模做一下。
注意减出来可能是负数,答案=(答案+100003)%100003。
为什么要取余?参考下面快速幂的说法,防止越界报错。

参考网址:
原题来源及解析:BZOJ 1008 [HNOI2008]越狱
快速幂算法(全网最详细地带你从零开始一步一步优化)

import java.util.Scanner;
public class Main {
    private static int mod = 100003;
    public static int quickMi(int base, int power){
        // 快速幂算法,返回base^power
        // 这道题而言,反正都要mod 100003,int足够了
        int result = 1;
        while(power > 0){
            if((power&1) == 1) result = result * base % mod; // 此处等价于power%2==1
            power >>= 1; // 等价于power /= 2;
            base = base * base % mod;
        }
        return result;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 犯人数
        int m = sc.nextInt(); // 编号数/宗教数
        int ans = quickMi(m,n) - m * quickMi(m-1,n-1);
        if(ans<0) ans = (ans+mod) % mod; // 相减可能为负的情况
        System.out.println(ans);
    }
}

T4

在这里插入图片描述
在这里插入图片描述
解题思路:
拿到题的最直观的想法是:将每一个物品 i i i与之后的所有物品 j ∈ [ i + 1 , ) j \in [i+1,) j[i+1,)的所有属性 k k k一一比较,若一致,则加 1 1 1。时间复杂度为 O ( n 2 k ) \mathcal{O}(n^{2}k) O(n2k)
但想到,可利用HashMap查找降低 n n n维到 l o g ( n ) log(n) log(n)维,于是有以下构造想法:
要用HashMap还要解决一个问题,若令:
a i , 1 + a j , 1 = a i , 2 + a j , 2 = ⋯ = a i , k + a j , k = c a_{i,1}+a_{j,1} = a_{i,2}+a_{j,2}= \dots = a_{i,k}+a_{j,k} = c ai,1+aj,1=ai,2+aj,2==ai,k+aj,k=c
c c c会随着 i , j i,j i,j而变化,无法检索到HashMap的key值。因此变换成:
0 = a i , 2 − a i , 1 + a j , 2 − a j , 1 = ⋯ = a i , k − a i , 1 + a j , k − a j , 1 0 = a_{i,2}-a_{i,1}+a_{j,2}-a_{j,1} = \dots = a_{i,k}-a_{i,1}+a_{j,k}-a_{j,1} 0=ai,2ai,1+aj,2aj,1==ai,kai,1+aj,kaj,1
这样,每次先得到一个 k − 1 k-1 k1的特征向量,需要检索的key值是它对应元素的相反数。
另外,检索保持方向一致,具体来说是枚举的逆方向,每次检索key值包含的信息量分别是: 1 , 2 , 3 , 4 , . . . , n − 1 1,2,3,4, ... ,n-1 1,2,3,4,...,n1
注意的是:java的HashMap除了字符串,HashCode(key值)中存储的都是地址值,例如int[] a = {1,2,3}; int[] b = {1,2,3}a.equals(b)-->false无法比较。对于字符串String a = "123"; String b = "123"; a.equals(b) --> true。因此对int数组的HashMap查找,需要对int数组进行处理。
这个链接例举了三种如果将byte[]作为HashMap key的处理方法
由于题目说明 2 ⩽ k ⩽ 10 2 \leqslant k \leqslant 10 2k10,因此我用了int[]转字符串的方法。

import java.util.Scanner;
import java.util.HashMap;

public class Main {
	public static String toString(int[] key) {
	        // 构造一个将int数组转化为String的方法
	        // 因为HashMap中的Hashcode(key值)如果不是字符串形式,统一存储的是地址值。
	        StringBuilder sb = new StringBuilder();
	        for (int i = 0; i < key.length; i++) {
	            sb.append(key[i]);
	        }
	        String str = sb.toString();
	        return str;
	    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // n个物品
        int k = sc.nextInt(); // 每个物品k个属性
        HashMap<String, Integer> map = new HashMap<>();
        int res = 0; // 记录共有多少对完美对
        int[] tmp = new int[k - 1]; // 储存新的k-1个特征
        int[] key = new int[k - 1]; // 需查找的key值
        String strTmp = "";
        String strKey = "";
        for (int i = 0; i < n; i++) {// 遍历每一个物品
            int a0 = sc.nextInt();
            for (int j = 1; j <= k - 1; j++) {
                tmp[j-1] = sc.nextInt() - a0; // a_ij - a_i0
                key[j-1] = -tmp[j-1];
            }
            strKey = toString(key);
            strTmp = toString(tmp);

            if(map.get(strKey)!=null){ // 能查找到key值
                res += map.get(strKey);
            }
            if(map.get(strTmp) == null){ // 如果当前特征没有在map中
                map.put(strTmp, 1);
            }else{ // 如果在map中,次数num加一,代表这个特征目前已有num+1个了
                map.replace(strTmp, map.get(strTmp)+1);
            }
        }
        System.out.println(res);
    }
}

这里贴一张以前收藏的String()、StringBuilder()的用法图
在这里插入图片描述

T5

在这里插入图片描述
在这里插入图片描述
思路:并查集。
并查集是一种树的数据结构,主要从路径压缩和按秩合并两个方向优化。这个代码我没有在考试中试过。还没做到这题就时间截止了。
并查集详细讲解链接,up主优秀

import java.util.Scanner;
public class Main {
    private static int N = (int) 1e7 + 5;
    private static int[] parent = new int[N]; // 该位置对应的父位置(不一定能指向根位置)
    private static int[] rank = new int[N]; // 以该位置为根的节点的秩
    private static int[] count = new int[N]; // 以该位置为根的树一共有多少个节点

    // 初始化关系,自己做自己的根
    public static void init() {
        for (int i = 0; i < N; i++) {
            parent[i] = i;
            rank[i] = 1;
            count[i] = 1;
        }
    }

    // 找到当前位置的根节点
    public static int findRoot(int x) {
        if (x == parent[x]) {
            return x;
        } else {
            // 路径压缩,减少下一次查询的时间
            parent[x] = findRoot(parent[x]); // 把沿途的父节点都设为根节点
            return parent[x];
        }
    }

    // 按秩合并
    public static void merge(int n, int m) {
        if (rank[n] == rank[m]) {
            rank[n]++;
            count[n] += count[m];
            parent[m] = n;
        } else if (rank[n] < rank[m]) { // n指向m
            parent[n] = m;
            count[m] += count[n];
        } else { // rank[m] < rank[n] m指向n
            parent[m] = n;
            count[n] += count[m];
        }
    }

    public static void main(String[] args) {
        // 并查集
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt(); // T组测试数据
        while (T > 0) {
            T--;
            init();
            int n = sc.nextInt(); // 当前测试数据有n对关系
            int preRoot = 0;
            int nextRoot = 0;
            for (int i = 0; i < n; i++) {
                preRoot = findRoot(sc.nextInt()); // 前一个节点对应的根节点
                nextRoot = findRoot(sc.nextInt()); // 后一个节点对应的根节点
                merge(preRoot, nextRoot);
            }
            // 遍历count找出以某个位置为根的树的最多节点数
            int res = 0;
            for (int i = 0; i < N; i++) {
                res = Math.max(res, count[i]);
            }
            System.out.println(res);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值