这是我想解决的问题,
您将获得一个包含2行和N列的表格。每个单元格中都有一个整数。分数 这样的表的定义如下:对于每列,考虑两个数字的总和 在专栏中;如此获得的N个数的最大值是得分。例如,对于 表
7 1 6 2
1 2 3 4得分为max(7 + 1; 1 + 2; 6 + 3; 2 + 4)= 9。 表的第一行是固定的,并作为输入给出。第二种可能的方法 行被认为是:
1; 2; ::: ;; ñ
2; 3; ::: ;; N; 1
3; 4; ::: ;; N; 1; 2
|
N; 1; ::: ;; ; N 1
例如,对于上面的示例,我们将以下各项视为第二行的可能性。
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
您的任务是找到第二行上述每个选项的分数。在里面 上面的示例,您将评估以下四个表,
7 1 6 2
1 2 3 4
7 1 6 2
2 3 4 1
7 1 6 2
3 4 1 2
7 1 6 2
4 1 2 3
并分别计算得分9,10,10和11测试数据:N <= 200000
时间限制:2秒
这是一个显而易见的方法:
维护两个阵列A,B,执行以下n次
此方法将花费O(n ^ 2)时间,外部循环运行N次,并且有两个内部循环,每个循环运行N次。
为了减少所花费的时间,我们可以在第一行中找到最大元素M(在线性扫描中),然后在A [i] + N <= M时移除A [i]和B [i] + 1.
因为他们永远不会是最大的。
但是这种方法在平均情况下表现可能更好,最坏情况时间仍然是O(N ^ 2)。
要在常量时间内查找max,我还考虑使用堆,堆的每个元素都有两个属性,它们的原始值和要添加的值。
但是,对于n个案例中的每一个,仍然需要一个线性时间来增加堆的所有元素的待添加值。
所以时间仍然是O(N ^ 2)
我无法找到能够比N ^ 2时间更快地解决这个问题的方法,因为N的值可能非常大,因此速度太慢。
任何帮助将不胜感激。
答案 0 :(得分:5)
还有O(n)算法。使用与之前答案中相同的观察结果:
现在考虑将第二行向左旋转时列的总和会发生什么变化(例如将其从1,2,...,N更改为2,3,...,N,1):每列总和将增加1,除了一列总和减少N-1。
我们可以将一列总和减少N,而不是修改所有列总和,然后取最大列总和加1来查找列总和的新最大值。所以我们只需要更新一列而不是所有列。
当我们迭代第二行的可能性时,出现最大值的列只能向左移动或跳回到具有总体最大值的列。候选列是从左到右最大扫描中的临时最大值。
以示例输入7,1,6,2算法运行如下: 步骤1计算总和8,3,9,6 步骤2从左到右找到临时最大值:col 1中为8,col 3中为9 第3步生成从右到左遍历数组的结果
8 3 9 6 -> output 9 + 0 = 9
8 3 9 2 -> output 9 + 1 = 10
8 3 5 2 -> current max col is decreased, previous max 8 is larger and becomes current
output 8 + 2 = 10
8 -1 5 2 -> output 8 + 3 = 11
以下是C:
中的算法#include <stdio.h>
int N;
int A[200000];
int M[200000];
int main(){
int i,m,max,j,mval,mmax;
scanf("%d",&N);
for(i = 0;i < N; i++){
scanf("%d",&A[i]);
A[i] = A[i]+i+1;
}
m = 0;
max = 0;
M[0] = 0;
for(i = 1;i < N; i++){
if(A[i] > A[max]){
m++;
M[m] = i;
max = i;
}
}
mval = A[max] - N;
mmax = max;
for(i = N-1,j = 0;i >=0;i --,j++){
printf("%d ", A[max]+j);
A[i] = A[i] - N;
if(i == max){
if (A[i] < mval) {
max = mmax;
} else if(m > 0 && A[i] < A[M[m-1]]){
max = M[m-1];
m--;
}
}
}
printf("\n");
return 0;
}
答案 1 :(得分:2)
以下是O(n*logn)
解决方案:
假设您计算第二行特定排列的所有列总和。
现在考虑将第二行向左旋转时列的总和会发生什么变化(例如将其从1,2,...,N
更改为2,3,...,N,1
):每列总和将增加1,但一列除外总和减少了N-1。
我们可以将一列总和减少N,而不是修改所有列总和,然后取最大列总和加1来查找列总和的新最大值。所以我们只需要更新一列而不是所有列。
所以我们的算法是:
将第二行设置为1,2,...,N并计算每列的总和。将所有这些总和放在最大堆中。堆的根将是最大的总和。
对于{1}中的i
N
:
N-i
列对应的堆节点的值减少N
。i
。每次堆更新需要O(logn)
,这会导致总时间O(n*logn)
。