阿里天池超级码力复赛
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;
}