Introduction
Dynamic programming is applicable to this problem, but unfortunately, it's still quadratic (though a significantly lesser quadratic in the usual case).
If you're curious, what tipped me off to DP is the one-directional dependency. In other words, if the voices could go in both directions rather than just forward, dynamic programming would not be possible since a pleasant recurrence relation could not be found.
I've assumed that you've used dynamic programming before and are familiar with it. If you are not, please let me know, and I will try to explain it in more detail.
Problem Consideration
Let's consider the formulation of the problem for a second:
There are n
people sitting at a table. Each person i
(1 <= i <= n
) is sitting at seat x(i)
, and each person has a voice volume t(i)
. A volume of t
means that the speaker's voice can reach at most t
seats (not people) directly. In other words, if a person is at seat 3 with a volume of 5, he can talk to everyone in between his own seat and seat 8 (including seat 8).
A conversation is started by a person p
, and it can consist of all people be reached by p
, including through intermediary speakers. In other words, if p
is at seat 3, and has a volume of 5, a person at seat 9 cannot hear him. If, however, someone is sitting at seat 5 with a volume of 5, p
can talk to seat 9
by way of the person in seat 5
(since 5 + 5 = 10; 10 >= 9
).
Note: I have used 1-indexing rather than 0-indexing.
Ok, so why am I wording it like this? Notice that there is a recursive relation here. p
can talk to anyone whom the people p
can talk to directly can talk to. In other words, for each person q
, if p
can talk to q
, then p
can also talk to all people to whom q
can speak. Like wise, for each person r
, if q
can speak to them, q
can speak to all of the people they can speak to.
In more natural terms, if Mark can talk to Jon and Jon can talk to Steve, then Mark can talk to Steve and everyone to whom Steve can talk.
Informal Solution
Let's formulate this in English and then we'll formulate it mathematically.
In short, the maximum number of people that person i
can speak with is the maximum number of people that the people he can directly speak with can speak with.
Imagine that i
can speak directly with j
, k
, and m
. There are three options:
1) j can speak with the most people
2) k can speak with the most people
3) m can speak with the most people
Note that picking j must implicitly include k (if you don't see why, ask me).
So, if you pick each option, i
can speak with this many people:
1) 1 (since i
can speak to himself) + the number of people with whom j can speak
2) 2 + the number of people with whom k can speak. The addition of two instead of is necessary since k's people do not include j
, but i
can obviously speak with j
.
3) 3 + the number of people with whom m can speak. Same logic: j, k, and all of the people with whom m can speak (including m)
Or, in general, for each person, p
, to whom i
can talk you must consider to how many people p
can talk, and add it to the number of people that are between i
and p
. The maximum of this is the choice you should make.
So, now we should be asking ourselves what the base case is.
The last person can only speak to himself as there is no one past him. This is our base case, and thus our dyamic programming loop will start at the end of all of the people and go back to the beginning.
Formal Solution
Let's formulate this a bit more mathematically now.
For the sake of ease, let's consider a different perspective of the same data: how many people could chair c
talk to?
- Let
m(c)
be the number of people to whom chair c
can speak.
- Let
v(c)
be the number of chairs that c can reach directly.
Let f(c, d)
be the number of occupied chairs between c and d, exclusive.
If chair c
is empty, m(c) = 0
If chair c
is the last chair, m(c) = 1
Otherwise,
m(c) = 1 + max{f(c, j) + m(c + j)} for 1 <= j <= v(c)`
Let's break down the third case, since it's a bit complicated:
- We have 1 since
c
can always talk to itself if it's non-empty.
f(c + 1, j - 1)
is the number of occupied chairs between i and j, not including i or j. This is necessary since c
can obviously talk to all of the people between it and j
(if it can talk to j
), but picking chair j
does not count all of these people.
m(c + j)
is the recursive (or dynamic programming). It is just the number of people to whom chair c + j
can speak. Note that this will always just be a constant-time look up since all m(d)
are already known for d > c
.
- Ranging from
1 to v(c)
is necessary so we consider every chair to which chair c
can speak.
That was easy to formulate, but unfortunately, with up to a billion chairs, it's not very feasible. Instead, we need to formulate it in terms of person i
rather than chair c
. Luckily, this is pretty easy. The last person can only talk to 1 person. For every other person, just use similar logic as to the chairs, but be careful to hop from person to person rather than from chair to chair.
Run time analysis
Unfortunately, now that we've done all of this work, if we stop and think about it, we'll see that this is still quadratic. In the worst case, we must consider every person between the person of question and the last person.
In the non-worst case though, this will handily performance better (asymtotically anyway) than your solution. Your solution keeps going through people until it can no longer reach a person at all. This solution only goes through only people it can reach directly.