我遇到了算法复杂性的问题,我尝试下面的解决方案,当我忘记考虑时间限制时,认为这很容易。我在下面添加了我的代码,我甚至不确定它的复杂性。我很想知道O(N)
或O(K)
解决方案是什么。如果有人可以帮助解决这个问题,那将非常感激。
Time Limit: 1 second
有可用的K座位,每个座位由一个圆圈周围的实际座位代表。 K + 1人最初也站在圈子周围的点上。圆圈上的点从1到N顺时针标记,使得点1紧跟在点N之后。没有两个人最初站在同一点,并且没有两个椅子将在同一点。
每一秒,所有仍然站着的人都会做同样的事情(同时):
如果该人站在与空椅子相同的位置,则该人将坐在其中。
否则,此人将围绕圆圈顺时针移动一个位置到下一个点。如果该人先前在点i(i
由于有K + 1人,最终所有K座位将被占用,而一个人将无座位。坐在圈子的第一个座位的人将拥有最好的位置。 (圆圈中的“第一个”座位被定义为从第1点开始顺时针方向的第一个座位。)
你的任务是确定谁将成为第一个席位,谁将成为站起来的人。
输入
N和K.
K空格分隔的整数,表示有椅子的点,按升序排列。因此,此列表中的第一个主席将是第一个席位
K + 1以空格分隔的整数,代表人们的点,按递增顺序排列。根据他们在此列表中的位置,人们从1到K + 1编号。
1≤N≤1000000
1≤K≤100000
输出
第一个席位的人员
留下的人
Sample
Input
10 3
2 5 8
3 4 6 8
Output
3
1
在第一秒,第四个人(第8点)将立即坐在他们下面的椅子上。其他三个人将在圈子周围移动一个地方。在下一秒,第二个人(现在位置5)将坐在第二个座位上。第一个和第三个人将继续绕着圆圈走,直到第三个人到达位置2并坐下,让第一个人没有椅子坐下。
while(standing != 1)
{
for (int i = 0; i < K + 1; i++)
{
if (sitting[i] == 0)
{
people[i]++;
if (isSitting(people[i], chairs,i)) //this function checks if the current person is at a chair
{
sitting[i] = 1;
standing--;
}
if (people[i] > N)
{
people[i] = 1;
}
}
}
}
standingPerson = indexOf(sitting,K+1 ,0);
此尝试几乎在所有测试输入上超时,仅通过8/30个案例。
以下是一些使用其他用户建议的O(k)解决方案的代码。转移到c ++
int seat1 = chairs[0], best = -1, accum = 1;
int unlucky[] = {0, -1};
for (int pos; pos < K + 1; pos++) {
if (people[pos] <= seat1) {
best = people[pos] + 1;
accum -= 1;
} else
break;
}
if (accum < 0) {
unlucky[0] = accum;
unlucky[1] = 1;
}
int i = K, j = K - 1;
while (i >= 0 && people[i] > seat1) {
if (chairs[j] >= people[i]) {
accum += 1;
j -= 1;
} else {
accum -= 1;
i -= 1;
}
if (best == -1 && accum == 0) {
best = i + 2;
}
if (accum < unlucky[0]) {
unlucky[0] = accum;
unlucky[1] = i + 2;
}
}
fprintf(out_file, "%d\n%d", best, unlucky[1]);
答案 0 :(得分:1)
首先让我们通过将表视为两个circular buffers来简化这个问题。一个用于椅子,一个用于人。我们可以说缓冲区包含椅子'C',人'P'和空白点'E'。
因此,假设我们说循环缓冲区从1开始,您的示例可以重写为
E C E E C E E C E E
E E P P E P E P E E
您当前的解决方案最糟糕的情况是O(N*K)
。想象一下这样的配置:
P P E E E E E E E
E E E E E E E E C
您的算法通过将人员缓冲区移动七次来解决上述问题。你通过遍历所有人并逐个转移来实现转变。因此,您的算法会使O(N)
(桌面大小)发生变化,每次轮班都会将所有O(K)
人移动一个位置。因此O(N*K)
。虽然,我认为这是一个非常悲观的上限,你平均会更快。
我认为一个好的O(K)
可以在两个指针的帮助下实现。指向人员缓冲区的指针和指向主席缓冲区的另一个指针。我们的想法是分别通过两个缓冲区,并将一个等待的人与一把可用的椅子相匹配。这个想法是,无论何时你看到椅子,如果有人在等,那么你可以将他与那把椅子相匹配。如果没有人在等待,那么您将椅子添加到可用椅子的队列中。如果在我们通过两个缓冲区之后有可用的椅子和可用的人员,那么我们知道这些人需要通过桌子的1
点找到一把椅子,我们可以将它们与可用的匹配将最后一个等候人员与第一把椅子相匹配的椅子。
除了两个指针,您还可以使用两个队列来跟踪可用的椅子和等待的人。下面是解决方案的伪代码版本。
queue<Person> personQueue;
queue<Chair> chairQueue;
int cptr=0, pptr=0;
while(cptr < K || pptr < K+1)
if (cptr >= K) {
//no chairs ahead of us only behind us
personQueue.addAllRemainingPersons()
}
else if(pptr >= K+1 || chair[cptr] < person[pptr]) {
// the currently pointed at chair is at a lower position
// than the pointed at person,
// so we match this chair with a waiting person instead
match(personQueue.back, chair[cptr]);
cptr++;
}
else {
//add person to the waiting-for-chair queue
personQueue.push_back(person[pptr]);
pptr++;
}
}
// at this point we have a situation with many chair in front of many persons
// for example
// CCCCPPPPP
// This we solve by matching the last person with the first chair
// until only one is left
while(!cQueue.empty()) {
match(personQueue.back, chairQueue.front);
}
确定谁坐在第一把椅子上的人可以在匹配功能中发生,在那里你有足够的信息可以告诉他们。仍然站着的人是唯一一个没有找到座位的人(唯一一个我们不会称之为匹配的人)。
答案 1 :(得分:0)
要确定谁将在第一个座位,我们需要计算从第一个座位开始的座位/人员的反方向,并在人数等于座位数时立即停止。为了实现这一点,我们需要一个累加器,当遇到座位时它会增加,当遇到一个人时会减少。当此累加器为零时停止。
为了确定谁将是站起来的人,我们可以继续以相同的方式更新累加器,并找到该累加器第一次获得其最小值的位置。对于任何人的位置,这个累加器不是最小的(或者当这个最小值接近数组的开头而不是其他最小值时),我们可以在数组中稍后找到该累加器具有相同值的位置,这意味着有足够的位置让这个人就座。
有关详细说明,请参见下图。它显示所有可能的座位/人员位置的累加器值。虽然算法只遍历输入数组一次,但此图表显示这些数组的累加器值经过三次,因此可以看到问题的周期性。
每个数组遍历的值彼此相似。唯一的区别是,每当我们到达圆圈上的相同点时,值减少一个。
很容易注意到除了一个之外的所有累加器值都具有以下属性:我们总是可以在圆圈中找到另一个具有相同值的点。这意味着座位数和这两点之间的人数彼此相等。这意味着在这两点之间具有起始位置的每个人也将坐在这两点之间的某个位置:右边的所有人都远离这个间隔并且不假装占据这些座位。左边的所有人来得太晚了。
这些等值点之间的间隔的一些有趣情况是:(1)当间隔越过阵列边界时,(2)两个最小值之间的间隔,(3)当空座位在人的旁边或者如果他们是在同一点。
只有一个位置(4)对应于最后一个最小累加器的值(如果我们向后搜索,则为第一个)不具有此属性。没有任何意义,右边的价值相同。这个初始职位的人将保持站立状态。
以下是Python中的工作代码:Ideone link。
def solve(k, s, p):
seat1 = s[0]
best = -1
unlucky = (0, -1) # min(accum), position
accum = 1
# process all persons between start of the array and the seat #1 position
for i, pos in enumerate(p):
if pos <= seat1:
best = i+1
accum -= 1
else:
break
if accum < 0:
unlucky = (accum, 1)
# process all seats/persons in reverse direction
i = k
j = k-1
while i >= 0 and p[i] > seat1:
if s[j] >= p[i]: # a seat
accum += 1
j -= 1
else: # a person
accum -= 1
i -= 1
if best == -1 and accum == 0:
best = i+2 # +1 because indexing starts with 0 & +1 because of pre-decrement
if accum < unlucky[0]:
unlucky = (accum, i+2)
return (best, unlucky[1])
print(solve(3, [2,5,8], [3,4,6,8]))
由于每个座位/人员仅被检查一次,因此时间复杂度为O(k)。