zoukankan      html  css  js  c++  java
  • 阿里天池超级码力复赛

    9-13阿里天池超级码力复赛

    只做了一题,赛后又做了一题。稍微记录一下过程

    选美

    大致题意:有n名选手参加选美比赛,每位选手展示需要p分钟,所有选手展示完后,评委们选出一个第一名,评委评第一名需要v分钟,例如:有n = 10位参赛选手,有p = 1分钟展现身材,评委花v = 1分钟评出最优,一共花费10 * 1+1=11分钟。由于参赛的选手非常多,现在可以对选手进行分组,将每组的第一名晋级到下一轮。例如:假设10个人分成两组,一组7人,另一组3人,分别需要8分钟和4分钟评比出结果,显然3人组需要等7人组完成评比,然后再花3分钟评比出结果 小栖手下有足够多的评委,各小组可以同时进行小组内的评比,小组可以再分成更多的小组,问最快需要花多久可以完成评比。
    其中 1 <= n <= 10^15 , 1 <= p,v <= 1000

    输入:n,p,v

    例如:输入n = 6, p = 1, v = 2 输出 8

    解题思路:假设n = a * b, 则n作为整组进行比赛的时间是n * p + v,而分成a组每组b个人时间是a * p + v + b * p + v(分成b组每组a个人同理)。则这可以初步看成一个因数拆分的问题。例如n = 900,可以拆成2 * 2 * 3 * 3 * 5 * 5,轮次 = 1时,时间是n * p + v,轮次 = 2时,时间是30 * p + 30 * p + 2 * v,轮次 = 3时,时间是 10 * p + 10 * p + 9 * p + 3 * v。这样便可以求解。
    但是这题还有一个问题,如果n是一个非常非常大的质数,这样的方法就会有问题,而n可以拆成a * b不一定n = a * b, 例如29人也可以拆成5组,每组6人,有一组少一个人并不影响时间计算情况。
    所以这题变成了开次方根的问题。代码如下:

    public long shortestTime(long n, int p, int v) {
        if (n == 1) {
            return 0;
        }
        long res = n * p + v;
        if (n <= 4) {
            return res;
        }
        // i 为轮次
        for (long i = 2; i < 1000; i++) {
            double a = 1d / (double)i;
            double q = Math.pow((double)n, a);
            // q 为 次方根
            long l = (long)q, r = (long)Math.ceil(q);
            // 如果 q是整数,直接计算分成i轮的时间
            if (l == r) {
                res = Math.min(res, i * l * p + i * v);
            } else {
                // 当q不是整数时,我们设l为q去掉小数部分,r = l + 1
                // 此时我们希望l尽可能多,所以遍历一遍
                for (long j = 0; j <= i; j++) {
                    // 当j个r和(i - j)个l相乘大于n时,计算时间当作分成i轮的最短时间
                    long t = pow(r, j) * pow(l, (i - j));
                    if (t >= n) {
                        long temp = (j * r + (i - j) * l) * p + i * v;
                        res = Math.min(res, temp);
                        break;
                    }
                }
            }
        }
        return res;
    }
    
    private long pow (long a, long b){
        if (b == 0) {
            return 1;
        } else {
            return a * pow(a, b - 1);
        }
    }
    

    密钥

    大致题意:有一串数字字符串(只包含0-9),这串数字串隐藏着一个密钥,取此数字串的一个子串,使得子串中出现最多次数的数字和出现最少次数的数字之差的最大,这个最大值就是数字串代表的密钥 你能找到这个密钥吗?

    样例 1:

    输入:
    s = "11100"
    输出:
    2
    解释:
    选取子串”1110“,其中包含3个1,1个0,3-1=2

    样例 2:

    输入:
    s = "1110"
    输出:
    2
    解释:
    选取子串”1110“,其中包含3个1,1个0,3-1=2

    解题思路:这题基本的思路是动态规划,用dp[i][j][k]表示到第i个字符串为止,j字符比k字符多的个数。再用一个visited[i][j]表示字符j到i位置为止有没有出现过。最后内存超了。赛后优化了几个地方,并请教了一些大佬。实际上只需要dp[j][k]用来记录j比k多的个数,不需要每个位置都记录。另外用定义一个visited[n][2],其中visited[i][0]表示字符i在到目前为止有没有出现过,visited[i][1]表示目前dp[j][k]记录的过程中,i有没有出现过。下面是代码:

    public int key(String s) {
        int[][] dp = new int[10][10];
        boolean[][] visited = new boolean[10][2];
        int max = 0;
        for (int i = 0; i < s.length(); i++) {
            int k = s.charAt(i) - '0';
            visited[k][0] = true;
            visited[k][1] = true;
            for (int j = 0; j < 10; j++) {
                if (j != k) {
                    if (dp[k][j] >= 0) {
                        dp[k][j]++;
                    } else {
                        visited[j][1] = false;
                        dp[k][j] = 1;
                    }
                    if (visited[j][1]) {
                        max = Math.max(max, dp[k][j]);
                    } else if (!visited[j][1] && visited[j][0]) {
                        max = Math.max(max, dp[k][j] - 1);
                    }
                    dp[j][k]--;
                    max = Math.max(max, dp[j][k]);
                }
            }
        }
        return max;
    }
    
  • 相关阅读:
    微服务架构有哪些优势?
    Java 线程数过多会造成什么异常?
    Java 死锁以及如何避免?
    抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized 修饰?
    内部类可以引用它的包含类(外部类)的成员吗?有没有 什么限制?
    CSS选取第几个标签元素:nth-child、first-child、last-child
    数据库约束
    DQL查询语句
    网络编程(客户端,服务端文件上传下载)
    缓冲流,转换流
  • 原文地址:https://www.cnblogs.com/liusandao/p/13677695.html
Copyright © 2011-2022 走看看