E-Maxx Algo - Ru.en
E-Maxx Algo - Ru.en
Welcomes you book collected on this Site e-maxx.ru/algo (As of 26 Apr 2012 1:48).
In this book you will find the description, implementation, and evidence for a variety of algorithms, from the famous to all
those that are the lot of the best specialists in olimpiadnikov and Computer Science. At the end are links to thematic literature,
which can be downloaded from my site, as well as some information about me.
Happy reading!
Table of contents
Algebra
elementary algorithms
● Euler's function and its calculation
● Binary exponentiation in O (log N)
● Euclid's algorithm of finding the GCD (greatest common divisor)
● Sieve of Eratosthenes
● Advanced Euclidean algorithm
● Fibonacci numbers and their rapid calculation
● Reverse ring element modulo
● Gray code
● Long arithmetic
● Discrete logarithm modulo M algorithm baby-step-giant-step Shanks for O (sqrt (M) log M)
sophisticated algorithms
● The simplicity of the test BPSW numbers in O (log N)
● Efficient algorithms for factorization Pollard p-1, Pollard p, Bent, Pollard Monte Carlo Farm
● Fast Fourier Transform for the O (N log N). Application to the multiplication of two polynomials or long numbers
Counts
elementary algorithms
● Search width
● Dfs
● Topological Sort
● Search the connected components
● Leviticus algorithm finding the shortest paths from a given vertex to all other vertices in O (NM)
Cycles
● Finding a negative cycle in the graph in O (NM)
● Finding Euler Euler path or cycle in O (M)
● Checking on the acyclic graph and finding cycle for O (M)
● Lowest common ancestor. Finding the O (1) with preprocessing O (N) (algorithm Farah-Colton and Bender)
● Problem RMQ (Range Minimum Query - at least in the interval). Solution in O (1) preprocessing with O (N)
● Lowest common ancestor. Finding the O (1) in the offline (Tarjan algorithm)
coherence
● Edge connectivity. Properties and being
● Vertex connectivity. Properties and being
● Graphing with the stated values of the vertex and edge connectivity and the lowest degree of the vertices
● The inverse problem of MST (inverse-MST - inverse problem the minimum spanning tree) in O (N M2)
miscellanea
● Paint edges of the tree (data structure) - decision in O (log N)
● Task 2-SAT (2-CNF). The decision is O (N + M)
● Heavy-light decomposition
Geometry
elementary algorithms
● Length of association on the line segments in O (N log N)
● The sign area of a triangle and the predicate 'Clockwise'
● Checking on the intersection of two segments
● Finding the equation for a straight line segment
● Finding the point of intersection of two lines
● Finding the point of intersection of two segments
● Finding the area of a polygon in a simple O (N)
● Pick's theorem. Finding the square lattice polygon in O (1)
● Segments covering problem points
● Centers of gravity of polygons and polyhedra
● Voronoi diagram in the two-dimensional case, its properties, and application. A simple algorithm for constructing O (N4)
● Finding all the faces, the outer edge of a planar graph in O (N log N)
● Finding the closest-pair algorithm, divide-and-conquer in O (N log N)
● Geometric transformation inversion
● The search for common tangent to two circles
● Find pairs of intersecting segments algorithm swept by lines for O (N log N)
Line
● Z-line function of demand and its calculation for the O (N)
● Prefix function, its calculation and application. Algorithm Knuth-Morris-Pratt
● Hashing algorithms in problems on the line
● Rabin-Karp algorithm for the substring search for O (N)
● Expression parsing in O (N). Reverse Polish Notation
● Suffix array. Building for the O (N log N) and application
● Suffix automaton. Building for the O (N) and application
● Finding all subpalindromes for O (N)
● Lyndon decomposition. Duval algorithm. Finding the smallest cyclic shift for the O (N) time and O (1) memory
● Algorithm Aho-Korasik
● Suffix tree. Ukkonen's algorithm
● Search all tandem repeats in a string algorithm Maine-Lorentz (divide-and-rule) for the O (N log N)
Data Structures
● Sqrt-decomposition
● Fenwick tree
● System of disjoint sets
● Segment tree
● Cartesian tree (treap, deramida)
● Modification of the stack and queue to retrieve the minimum in O (1)
● Randomized heap
Algorithms on strings
● Problem RMQ (Range Minimum Query - at least on the interval)
● Finding of the longest increasing subsequence in O (N2) and O (N log N)
● K-th order statistic in O (N)
● Finding of the longest increasing subsequence in O (N2) and O (N log N)
Dynamics
● Dynamics of the profile. Task "parquet"
● Finding the greatest zero submatrix in O (NM)
Linear algebra
● Gauss solution of linear equations for the O (N3)
● Finding the rank of a matrix in O (N3)
● Calculating the determinant of the Gauss method for the O (N3)
● Calculating the determinant method for Kraut O (N3)
Numerical Methods
● Integration by Simpson's formula
● Search for roots by Newton's method (tangent)
● Ternary search
Combinatorics
● Binomial coefficients
● Catalan numbers
● Necklaces
● The alignment of elephants on a chessboard
● Right parenthesis sequence. Finding the lexicographically next, K-oh, number identification
● Number of labeled graphs, connected labeled graphs, labeled graphs with K connected components
Game theory
● Games on arbitrary graphs. Method for retrospective analysis of O (M)
● Theory Shpraga Grande. It
Timetables
● Johnson's problem with one machine
● Johnson's problem with two machines
● Optimal selection of jobs in certain completion times and durations of execution
Miscellanea
● Josephus problem
● Game Fifteen: the existence of solutions
● Stern-Brocot tree. Farey series
● Search subsegments array with a maximum / minimum amount for O (N)
Application
● Literature
● About the Author
Euler function
Definition
Euler function (Sometimes denoted or ) - Is the number of properties from,
. In other words, the quantity of such properties in the
relatively prime to interval ,greatest common divisor
which is equal to unit.
The first few values of this function (A000010 encyclopedia OEIS)
Properties
The following three simple properties of the Euler - enough to learn how to calculate it for any number:
● If - Prime, then .
(This is evident, since any number except the Relatively easy with him.)
(As to the number of not only are relatively prime numbers of the form Which
pieces.)
Implementation
The simplest code calculating the Euler function, factoring in the number of elementary method :
The key place for the calculation of the Euler function - is finding factorization number . This is achieved in a
Euler's theorem often occurs in practical applications, for example, see Inverse element in the modulo.
The most obvious generalization - the remains of some modulo (obviously, the
associativity is preserved). Next in "popularity" is a generalization to the matrix product
(it is well-known associativity).
Algorithm
Note that for any number and even number doable obvious identity (which follows from
the associativity of multiplication)
It is the main method in binary exponentiation. Indeed, even for we showed how spending just one
multiplication operation can reduce the problem to a lesser extent twice.
It remains to understand what to do if the power odd. Here we do is very simple: move on to
power Which will have an even:
So, we actually found the recurrence formula: the degree we move, if it is even to And otherwise -
. It is understood that there will be no
to more than transitions before we come to (Based
recursive formula). Thus, we have an algorithm that works for multiplications.
Implementation
A simple recursive implementation:
This implementation can be further simplified somewhat by noting that the construction of the
square is always performed, regardless of the condition worked odd or not:
Finally, it is worth noting that the binary exponentiation is implemented by Java, but only for a class of long arithmetic BigInteger
both , And. English "greatest common divisor" is spelled "greatest common divisor", and its symbol is a common :
(Where the symbol " "Denotes divisibility, ie" "Denotes" divides ")
When it out number is zero, and the other is non-zero, their greatest common divisor, by definition, it is the second number. When
the two numbers are zero, the result is undefined (infinite any suitable number), we assume in this case, the greatest common
divisor of zero. Therefore we can talk about this rule: if one of the numbers is zero, then their greatest common divisor is the second
number.
Euclid's algorithm, discussed below, solves the problem of finding the greatest common divisor of two numbers
and for .
This algorithm was first described in the book of Euclid's "Elements" (about 300 BC), although it
may, this algorithm has an earlier origin.
Algorithm
The algorithm is extremely simple and is described by the following formula:
Implementation
Using the ternary conditional operator C + +, the algorithm can be written even shorter:
Proof of correctness
Please note that for each iteration of the Euclidean algorithm its second argument is strictly decreasing
Consequently, since it is non-negative, then the Euclidean algorithm always ends.
For correctness proofs we need to show that for any
.
We show that the quantity in the left-hand side is divided by the present on the right and the right-
hand - is divisible by standing on the left. Obviously, it would mean that the left and right sides of the
same, and that proves the correctness of Euclid's algorithm.
an
We now use the following simple fact: if for any three numbers complete d ,
and then performed: . In our situation, we obtain
So, we spent half the evidence to show that the left side of the right divides. The second
half of the proof is similar.
Operation time
Time of the algorithm is evaluated Theorem LameWhich establishes a surprising connection
Euclid's algorithm and the Fibonacci sequence:
If and for some , The Euclidean algorithm performs no more recursive calls. Moreover,
we can show that the upper bound of this theorem - optimal. At
it will be performed recursive call. In other words, serial
Fibonacci numbers - the worst input for the Euclidean algorithm.
Given that the Fibonacci numbers grow exponentially (as a constant raising ), We find that the
Euclidean algorithm runs in multiplication operations.
Thus, the calculation of the NOC can also be done using the Euclidean algorithm, with the same asymptotic behavior:
(Here divided into profitable first, and only then are multiplied by Because it helps to avoid
overflow in some cases)
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
Sieve of Eratosthenes
Sieve of Eratosthenes - an algorithm for finding all primes in the interval for
operations.
The idea is simple - to write a series of numbers And will strike out the first all numbers divisible by Except the number
And then dividing by Except the number , Then , Then , , And all other simple to .
Implementation
Immediately give the implementation of the algorithm:
int n;
vector <char> Prime (n
+1,true); prime[0] = Prime[1] =
false;
for (int i =2; i <= n; +
+ I) if
(prime[i])
if (i * 1ll * i <= n)
for (int j = i * i; j <= n;
j + = i) prime[j] =
false;
This code first checks all numbers except zero and one, as simple, and then begins the process of
sifting composite numbers. To do this, we loop through all the numbers from to And if the current
number of prime, then mark all the numbers that are multiples of him as a constituent.
At the same time we start walking from as fewer multiples necessarily have a prime divisor
In the
is smaller and hence, they have been eliminated previously. (But because it is easily cram type code
before the second nested loop is an additional check using type .)
With this implementation, the algorithm uses memory (obviously) and performs Action
(This is proved in the next section).
Asymptotics
We prove that the asymptotic behavior of the algorithm is .
So, for each prime will run the inner loop, which make actions. Consequently, we
need to estimate the following value:
Let us recall here two known facts: that the number of primes less than or equal to approximately equal And that -
th prime number is approximately equal to (This follows from the first statement). Then the sum can be written
thus:
Here we have identified the first prime of the sum, since the according approximation will ,
resulting in a division by zero.
We now estimate that amount by the integral of the same function on from to (We can produce
such an approximation, because, in fact, refers to the amount of his integral
approximation formula of rectangles):
Primitive integrand has . Performing substitution and removing members of the smaller
order, we obtain:
QED.
More rigorous proof (and provide a more accurate estimate of up to constant factors) can be found in the book
of Hardy and Wright "An Introduction to the Theory of Numbers" (p. 349).
On the asymptotic behavior of this optimization does not affect (indeed, repeating the above proof, we obtain an
estimate that, by the properties of logarithms, asymptotically is the same), although
the number of transactions decreased markedly.
Block sieve
Optimization of "simple screening to the root" implies that there is no need to store all the time all
array . To perform screening sufficient to store only simple to root out, that
is And the remainder of the array building block at a time, keeping the current time
Only one block.
blocks, the first
Let - Constant that determines the size of the block, then all will block
) Contains the numbers in the
( interval . Will process the blocks at a time, ie
for each of Unit will go through all the simple (from to ) And perform their screening only
within a current block. Gently handle the first unit costs - first, from simple should not
remove themselves, and secondly, the number and should be marked as not particularly simple. When processing the last
block should also not forget that the last desired number not necessarily in the end block.
We present the implementation of the sieve block. The program reads the number and finds a number of simple to :
int n;
cin >> N;
int nsqrt = (int) sqrt (n + .0);
for (int i =2; i <= nsqrt; + +
I)
if (! Nprime[i]) {
primes[cnt + +] =
I;
if (i * 1ll * i <= nsqrt)
for (int j = i * i; j <= nsqrt;
j + = i) nprime[j] =
true;
}
int result = 0;
for (int k =0, Maxk = n / S; k <= maxk;
+ + K) { memset (bl, 0, sizeof
bl);
int start = k * S;
for (int i =0; i <cnt; + + I) {
int start_idx = (start + primes[i] - 1) / Primes[i];
int j = max(start_idx,2) * Primes[i] - Start;
for (; j <S; j + =
primes[i]) bl[j]
= true;
}
if (k == 0)
bl[0] = Bl[1] = true;
for (int i =0; i <S && start + i <=
n; + + I) if (! Bl[i])
+ + Result;
}
cout << Result;
Asymptotics sieve block is the same as usual and the sieve of Eratosthenes (unless, of course, the size of units is not
will be quite small), but memory usage will be reduced to and decrease "wandering"
from memory. On the other hand, for each block for each of simple will run the division,
that will greatly affect in a smaller unit. Consequently, the choice of the constants must
strike a balance.
Experiments show that the best speed of operation is achieved when the has a value of about
to .
Upgrade to linear time work
Eratosthenes algorithm can be converted to a different algorithm, which is already operational in linear time - see article
"Sieve of Eratosthenes with linear time work". (However, this algorithm has drawbacks.)
Advanced Euclidean algorithm
While "Normal" Euclid's algorithm simply finds the greatest common divisor of two numbers and , Extended
Euclidean algorithm finds the GCD also factors in addition to and such that:
Ie he finds the coefficients by which the GCD of two numbers expressed in terms of the numbers themselves.
Algorithm
Make the calculation of these coefficients in the Euclidean algorithm is simple enough to derive formulas for which
they are changed during the transition
from the pairs a pair (Percent sign we mean taking the remainder
from the division).
Thus, suppose we have found a problems for the new
solution couple :
Comparing this with the original expression of the unknown and , We obtain the desired expression:
Implementation
This is a recursive function, which still returns the GCD of the numbers and, but apart from that - as desired
coefficients and as function parameters passed by reference.
Recursion base - case . Then the GCD is, and obviously required coefficients and are equal and
respectively. In other cases, the usual solution works, and the coefficients are translated
at the above formulas.
Advanced Euclidean algorithm in this implementation works correctly even for negative numbers.
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
Fibonacci numbers
Definition
The Fibonacci sequence is defined as follows:
History
These numbers are entered into in 1202 by Leonardo Fibonacci (Leonardo Fibonacci) (also
known as Leonardo of Pisa (Leonardo Pisano)). However, thanks to a 19th-century
mathematician Luca (Lucas) the name "Fibonacci numbers" became common.
However, the number of Indian mathematicians mentioned earlier in this sequence: Gopal (Gopala) to 1,135
city, Hemachandra (Hemachandra) - in 1150
Also botanically known phenomenon phyllotaxis''''. As an example, the location of sunflower seeds: if you look down on their
location, you can see simultaneously two series
spirals (like overlapping): some twisted clockwise, the other - against. It turns out that the number of these spirals is roughly
equal to two consecutive Fibonacci numbers: 34 and 55 or 89 and
144. Similar facts are true for some other colors, as well as pine cones, broccoli, pineapple, etc.
For many plants (according to some sources, 90% of them) are true and an interesting fact. Consider any leaf, and will descend
from him down until until we reach the sheet disposed on the stem in the same way (ie, directed exactly in the same direction).
Along the way, we assume that all the leaves, gets us (ie, located at an altitude between the start and end sheet), but arranged
differently. Numbering them, we will gradually make the turns around the stem (as the leaves are arranged on the stem in a
spiral). Depending on whether the windings perform clockwise or counterclockwise will receive a different number of turns. But
it turns out that the number of turns made by us in a clockwise direction, the number of turns made by counterclockwise, and
the number of leaves encountered form three consecutive Fibonacci numbers.
However, it should be noted that there are plants for which the above calculations give the number of very different sequences
and therefore can not be said that the phenomenon of phyllotaxis is the law - it
rather entertaining trend.
Properties
Fibonacci numbers have many interesting mathematical properties.
Here are some of them:
● Cassini ratio:
● Rule of "addition":
● GCD-equality:
● With respect to the Euclidean algorithm Fibonacci numbers have the remarkable property that they are the worst input
Fibonaccimal value
Zeckendorf theorem claims that every positive integer can be represented in a unique way as a sum of
Fibonacci numbers:
where , , , (Ie can not be used in the recording of two adjacent Fibonacci
numbers).
This implies that any number can be uniquely written Fibonacci system value, eg
obtain
Since modulo can only be different pairs, among this sequence there are at least two identical pairs. This
already means that the sequence is periodic.
We now choose among all such identical pairs of two identical pairs with the lowest numbers. Let this pair
with some rooms and . We prove that . Indeed, otherwise
a
n
for them there the previous couple d Which, by property of Fibonacci numbers as
are equal to each other. However, this contradicts the fact that we chose the matching pairs with the
lowest numbers, as required.
Literature
● Ronald Graham, Donald Knuth, and Oren Patashnik. Concrete Mathematics [1998]
Reverse ring element modulo
Definition
Suppose we are given a natural module And consider the ring formed by the module (i.e., consisting of a number of to
). Then for some elements of this ring can be found inverse.
It linear Diophantine equation of the second order. As shown in a related article from
terms that this equation has a solution which can be found using
The extended Euclidean algorithm (Hence the same incidentally, follows when Decisions, and therefore
and an inverse element does not exist).
On the other hand, if we take from both sides of the residue modulo , We obtain:
int x, y;
int g = gcdex (a, m, x, y);
if (g! = 1)
cout << "No solution";
else {
x = (x% m + m) % M;
cout << X;
}
Thus, we got the formula for direct calculation of the inverse. For practical applications typically use effective algorithm for
binary exponentiation, Which in this
If the quotient of the number is known, then this method also works for the asymptotic behavior .
.
.
.
opposite to the desired
Decision it is as follows. We denote number modulo.
Then for true identity:
r[1] = 1;
for (int i =2; i <m; + + I)
r[i] = (m - (m / i) * R[m% i] % M) % M;
Multiplying both sides by the inverse of and its inverse , We obtain the desired formula:
QED.
Gray code
Definition
Gray code numbering system is defined to be a non-negative integers, where codes of two
adjacent numbers differ in exactly one bit.
For example, for numbers of length 3 bits have a sequence of Gray codes: , , , , ,
, , . Eg .
This code was invented by Frank Gray (Frank Gray) in 1953.
int g (int n) {
return n ^ (n >> 1);
}
Applications
Gray codes have several applications in different areas, sometimes quite unexpected:
● Bit Gray code corresponds to a Hamiltonian cycle -Cube.
● In the art, Gray codes are used for minimizing the error when converting analog signals to digital signals (e.g. in
sensors). In particular, the Gray codes were discovered in connection with this application.
● Gray codes are used in solving the problem of Tower of Hanoi.
Let - number of disks. Let's start with the Gray code length, consisting of zeros (ie ), And will move
Gray Codes (from proceed to ). With every bit of the current CB Gray code -
first drive (and most significant bit corresponds to the smallest disk size and the oldest bat
- Maximum). Since at each step exactly one bit is changed, then we can understand the bit change as moving Th disc. Note
that for all drives except the smallest, at each step there is exactly
one option course (except for the starting and final products). For there is always the smallest drive
two versions of course, but there is progress in the strategy of choice, always leads to the answer: if
odd, then the sequence of movements of the smallest drive has the form (where
- Starter rod - The final rod - The remainder of the rod), and if even, then
.
● Gray codes are also used in the theory Genetic Algorithms.
Data structure
Keep long numbers will be in the form of a Vector of Numbers Where each element - is one digit number.
To increase efficiency in the system will work in base billion, i.e. each element of the vector
contains not one, but numbers:
The numbers will be stored in a vector in such a manner that at first there are the least significant
digit (ie, ones, tens, hundreds, etc.).
Furthermore, all the operations are implemented in such a manner that after any of them leading zeros
(ie excess leading zeros) are not (of course under the assumption that prior to each leading zeros is also
available). It should be noted that the implementation representation for the number zero is well
supported just two representations: an empty vector of numbers and digits vector containing a single
element - zero.
Output
The most simple - a conclusion of a long number.
First, we simply display the last element of the vector (or If the vector is empty), and then derive all
the remaining elements of the vector, supplementing them with zeros to characters:
(Here a small fine point: not to forget to write down the cast Because otherwise
number are unsigned, and if Then the subtraction overflows)
Reading
If the input number has leading zeros may be, they can remove, after reading the following way:
Addition
int carry = 0;
for (size_t i =0; i <max(a.size(), B.size()) | | Carry; +
+ I) { if (i == a.size())
a.push_back (0);
a[i] + = Carry + (i <b.size() ? b[i] : 0);
carry = a[i] > = Base;
if (carry) a[i] - = Base;
}
Subtraction
int carry = 0;
for (size_t i =0; i <b.size() | | Carry; + + I) {
a[i] - = Carry + (i <b.size() ? b[i] : 0);
carry = a[i] < 0;
if (carry) a[i] + = Base;
}
while (a.size() > 1 && A.back() == 0)
a.pop_back();
Here we are after subtracting remove leading zeros to maintain the predicate that they do not
exist.
int carry = 0;
for (size_t i =0; i <a.size() | | Carry; + +
I) { if (i == a.size())
a.push_back (0);
long long cur = carry + a[i] * 1ll * b;
a[i] = int (cur% base);
carry = int (cur / base);
}
while (a.size() > 1 && A.back() == 0)
a.pop_back();
Here we are after the division remove leading zeros to maintain the predicate that they do not
exist.
(Note: the way additional optimization. If speed is critical, you can try to replace two division one: count only the
integer portion of a division (in the code is a variable ), And then to calculate it by the remainder of dividing
(using a multiplication operation).
Typically, this technique allows faster code, but not very much.)
int carry = 0;
for (int i =(int)a.size()-1; i> =0; - I) {
long long cur = a[i] + Carry * 1ll * base;
a[i] = int (cur / b);
carry = int (cur% b);
}
while (a.size() > 1 && A.back() == 0)
a.pop_back();
to the prime divisors of the module, and another number - balance on the same module.
Usually, when this storage numerator and denominator have to use long arithmetic, but, however,
it is the easiest form - "classic" long arithmetic, although sometimes is sufficiently embedded 64-bit
numeric type.
where and - coprime (Note: if they are not relatively prime, then the algorithm described below is incorrect, although
Algorithm
So, we have the equation:
equation. Put
where - Is preselected constant (as it is chosen depending on the We will understand later). Sometimes
called "giant step" (as an increase in its per unit increases at once ), And in contrast, the
- "Baby step".
Obviously, any (The interval - it is understood that this range of values will suffice) can be
represented in a form wherein it will be sufficient for the values:
To solve the original equation, we need to find the appropriate values and That the values of the
left and right sides are aligned. In other words, it is necessary to solve the equation:
This problem is solved by the meet-in-the-middle as follows. The first phase of the algorithm: we can calculate
the function for all values of the argument, and we can sort these values. The second phase
algorithm: we will sort out the value of the second variable , Calculate the second function, and seek the
value of the predicted values of the first function using a binary search.
Asymptotics
First, we estimate the computation time of each of the functions. And she and the other comprises the
construction of the power that can be performed using algorithm binary exponentiation. Then both of
these functions, we can compute in time .
The algorithm in the first phase comprises calculating features for each possible value of
and further sorting of values that gives us the asymptotic behavior:
In the second phase of the algorithm is evaluated function for each possible value of and binary
search the array of values that gives us the asymptotic
behavior:
Now, when we combine these two asymptotic we get Multiplied by the sum and And
almost clear that the minimum is achieved when the Ie algorithm for optimal constant
should be chosen as follows:
Note. We could exchange roles and (ie the first phase to compute values of the function, and the second
- ), But it is easy to understand that the result will not change, and the asymptotic behavior of this we can not improve.
Implementation
Function They produce their own solution to the problem. This function returns a response (the number in the interval
), More precisely, one of the answers. Function returns If there are no solutions.
Here we are for the convenience of the implementation of the first phase of the algorithm used the data structure
"Map" (red-black tree) that for each value of the function argument holds In which
this value is achieved. Here, if the same value is achieved repeatedly recorded smallest of all the
arguments. This is done in order to subsequently, on the second phase of the algorithm is found in the
response interval .
Given that the argument of the first phase we iterates from one and up to And the argument of the second phase
moves from zero to , Then eventually we cover the whole set of possible answers, because
segment contains a gap. At the same negative response could happen, and
responses greater than or equal we can not ignore - should still be in the corresponding period
of the answers .
This function can be changed in the event if you want to find all solutions the discrete logarithm
problem. To do this, replace "map" on any other data structure that allows for a single argument
to store multiple values (for example, "multimap"), and to amend the code of the second phase.
An improved
At speed optimization can proceed as follows.
First, immediately struck by the uselessness of the binary exponentiation algorithm in the second phase.
Instead, you can simply make a variable and multiplies it every time .
Secondly, the same way you can get rid of the binary exponentiation, and the first phase: in fact, once is
enough to calculate the value of And then just multiplies it.
Thus, the logarithm in the asymptotic behavior will remain, but it will only logarithm associated with the data
structure (ie, in terms of the algorithm, sorting and binary search values) - ie it will be the logarithm of the
, Which in practice provides a notable acceleration.
int an = 1;
for (int i =0; i <n; + +
I) an = (an * a) %
M;
map <int,int> Vals;
for (int i =1, Cur = an; i <= n; +
+ I) { if (!
Vals.count(cur))
vals[cur] = I;
cur = (cur * an) % M;
}
Finally, if the module small enough, then we can do to get rid of the logarithm in the asymptotic
behavior - instead of just having got normal array.
You can also recall the hash table: on average, they work well for that, in general gives
asymptotics .
Linear Diophantine equations in two
variables
Diophantine equation with two unknowns has the form:
Degenerate case
A degenerate case we immediately excluded from consideration when. In this case, of course, either
the equation has infinite number of arbitrary decision, or it has no solution at all (depending on
or not).
States that when divided into , The Diophantine equation has a solution;
otherwise Diophantine equation has no solutions. This follows from the obvious fact that a linear combination of
two numbers still must be divisible by a common divisor.
Assume that divided into Then obviously holds:
We have described the decision in the case where the number and non-negative. If one of them or both are negative, then
we can proceed as follows: take their modulus and apply them to the Euclidean algorithm, as described above, and then
change the sign found and in accordance with the number sign and respectively.
bool find_any_solution (int a, int b, int c, int & X0, int & Y0, int & G) {
g = gcd (abs(a),abs(b), X0, y0);
if (c% g! = 0)
return false;
x0 * = c / g;
y0 * = c / g;
if (a < 0) x0 * = -1;
if (b < 0) y0 * = -1;
return true;
}
Then we note that, by adding to number while depriving from We do not violate the equality:
Obviously, this process can be repeated any number, ie all numbers of the form:
void shift_solution (int & X, int & Y, int a, int b, int cnt) {
x + = cnt * b;
y - = cnt * a;
}
int find_all_solutions (int a, int b, int c, int minx, int maxx, int miny,
int maxy) {
int x, y, g;
if (! find_any_solution (a, b, c, x, y, g))
return 0;
a / = g; b / = g;
It is also easy to add to this realization the withdrawal of all the solutions found: it is enough to enumerate in
segment increments, finding for each of them corresponding directly from
equation .
Indeed, we have the right to do the following transformation (see previous item):
Now consider the case when and are not coprime. Then, clear solution will exist not always (e.g.,
).
Let Ie their greatest common divisor (Which in this case is more than one).
Then, if not divisible by then no solution exists. In fact, for any left-hand side of the equation, ie
is Always divisible by, while the right side of it is not divisible, which implies
that there are no
solutions.
an
If divisible by then, dividing both sides of this (ie, dividing, d on), we arrive at
new equation:
an
wherein d already be relatively prime, and this equation we have learned to solve. We denote its solution
through.
Clearly, this is will also be a solution of the original equation. However, if Then it is
not only solution. It can be shown that the original equation will have exactly decisions, and they will
look like:
Summarizing, we can say that the number of decisions modular linear equation is either
Or zero.
Formulation
In its modern formulation of the theorem is as follows:
Let Wherein - Pairwise coprime.
Then the correspondence (between numbers and tuples) will be bijective. And, moreover, the operations
performed on the number You can perform the equivalent of the corresponding elements of tuples - by
independent operations on each component.
That is, if
then we have:
In its original formulation of this theorem was proved by the Chinese mathematician Sun Tzu around 100 BC
Namely, he has shown in the special case of modular equivalence of solutions of equations and solving a
modular equation (see Corollary 2 below).
Corollary 1
Modular system of equations:
Corollary 2
Consequence is the connection between the system of modular equations and one corresponding modular
equation: Equation:
(As above, it is assumed that , The number pairwise relatively prime, and -
An arbitrary integer)
Garner's algorithm
Of the Chinese remainder theorem that can replace operations on the number of operations on tuples. Recall,
It can be widely used in practice (in addition to direct application to restore the number he balances of the various modules),
as we thus can replace surgery in the long
arithmetic operations with an array of "short" numbers. Say, an array of elements "enough" to number about
with characters (if selected as the first's simple); and if as filter's
simple about a billion, then by enough already with approximately signs. But, of course, then
need to learn restore by this tuple. Corollary 1 shows that this
recovery is possible and, moreover, the only (provided ).
Garner's algorithm and an algorithm, allowing it to perform recovery and effectively.
We seek a solution in the form:
Already quite clearly visible pattern that is easier to express the code:
So we learned to calculate the coefficients during Itself is the answer - the number - can be restored
formula:
It should be noted that in practice almost always need to calculate the answer using Long arithmetic, But
coefficients themselves still calculated on the built-in types, and therefore the whole Garner
algorithm is very effective.
Implementation of the algorithm Garner
Preferred to implement this algorithm in Java, because it contains a long arithmetic standard, but because there are no
problems with the transfer of the number of the modular system in the usual number of
(Using a standard class BigInteger).
The following implementation of the algorithm Garner supports addition, subtraction and multiplication, and
supports the work with negative numbers (see notes about this after the code). Implemented a number of
customary translation desyatichkogo submission to a modular system and vice versa.
In this example taken after simple That allows you to work with numbers up to about .
final int SZ = 100;
int pr[] = new int[SZ];
int r[] [] = new int[SZ] [SZ];
void init() {
for (int x=1000*1000*1000, I=0; i<SZ; + +x)
if (BigInteger.valueOf(x).isProbablePrime(100)) pr[i+
+] = x;
[j])).intValue();
}
class Number {
public Number() {
}
if (can_be_negative)
if (result.compareTo( mult.shiftRight(1)) > =
0) result = result.subtract( mult );
return result;
}
}
Support for negative Number deserves mention (flag
function ). Modular scheme itself does not imply differences between positive
и negative numbers. However, it can be seen that if the answer to a specific problem modulo does not exceed half of the product of
all primes, the positive numbers will be different from the negative that positive numbers are obtained than this mid and negative -
more. So after we Garner classical algorithm compares the result to the middle, and if it is, then we deduce minus
и invert the result (ie, subtract it from the product of all prime, and print it already).
Finding factorial power divider
Given two numbers: and. Required to calculate what degree divider includes a number, ie find the
greatest such that divided into .
And so on, each The first term of the series gives one to answer, and the number of
Given that the factorization is performed in a very simple manner Obtain final asymptotics .
Ternary balanced system of value
Ternary balanced system of value - it is non-standard positional numeral system. The base of power , But it differs from the
usual ternary system that figures are
. Since the use to single digits very uncomfortable, it usually takes a special designation.
We agree to denote here minus one letter .
For example, by balanced ternary system is written as And by - As . Ternary balanced system value allows you
to record negative numbers without writing a single
"minus" sign. Balanced ternary system allows fractional numbers (eg, written as ).
Translation algorithm
Learn to translate numbers in balanced ternary system. For this we must first
It is clear that now we have to get rid of digits For which we note that Ie we can replace the two in
the current discharge on While increasing the next (ie the left of it in the natural record) discharge on . If
we move from right to left on the record and perform the above operation (in this case in some discharges can
overflow more In this case, naturally, "reset" extra triples in MSB), we arrive at a balanced ternary recording.
As is easily seen, the same rule holds true for fractional numbers.
More gracefully above procedure can be described as follows. We take the number of ternary,
adds to it an infinite number of And then from the result subtract each digit
one (already without any hyphens).
Knowing now the translation algorithm of conventional balanced ternary system, we can easily
implement the operations of addition, subtraction and division - simply reducing them to the
corresponding operations on ternary unbalanced numbers.
Factorial calculation modulo
In some cases it is necessary to consider on some simple module complex formulas, which may contain, including
factorials. Here we consider the case when the module relatively small. It is clear that this problem only makes sense if
the factorial and included in the numerator and the denominator of the fractions. Indeed, the factorial and all subsequent
vanish modulo But in fractions
all factors containing Can be reduced, and the resulting expression has to be different from zero modulo .
Thus, formally task such. Required to calculate the modulo a prime , Without taking into account all the multiple factors
included in the factorial. By learning to effectively compute a factorial, we can quickly calculate the value of various
combinatorial formulas (eg Binomial coefficients).
Algorithm
Let us write this "modified" factorial explicitly:
With this record shows that the "modified" factorial divided into several blocks of length (The last block may be
shorter), which are all identical, except for the last element:
Common part calculate blocks easily - it's just Which can be calculated or programmatically
theorem Wilson (Wilson) immediately find . To multiply these common parts
All units, need to elevate the obtained value exponentiation What can be done for operations
(Cf. Binary exponentiation; however, you will notice that we actually erect minus one in which-
the degree and therefore the result will always be either or , Depending on the parity index. Value
in the latter, incomplete block also can be calculated separately for . Only the last elements
blocks, we consider them carefully:
And again we come to the "modified" factorial, but smaller dimension (as much as was
full blocks and have their ). Thus, the calculation of the "modified" factorial we have reduced
operations to the calculation
for already . Expanding this recurrence relation, we obtain that
recursion depth is , Total asymptotics algorithm is obtained .
Implementation
It is clear that the implementation is not necessary to use recursion explicitly: as tail recursion, it is easy
to deploy in the cycle.
int s = m;
while (s> 0) {
You can use ... s ...
s = (s-1) & M;
}
The only exception for both versions of the code - the subpattern is zero, will not be processed. Processing it
will take out of the loop, or use a less elegant design, for example:
Let us examine why the above code really finds all of this mask subpattern, without repetitions in O
(number), and in descending order.
Let us have a current capturing subpattern And we want to move to the next subpattern. Subtract from the mask unit,
thus we remove the rightmost single bit, and all the bits to the right of him to put in . Next, remove all the "extra" bits set,
which are not included in the mask and therefore can not be included in the subpattern. Removal operation is
performed bit. As a result, we "cut off the" mask before the largest value that it can take, ie until after the next subpattern
in descending order.
Thus, the algorithm generates all subpatterns this mask in order strictly decreasing, spending on
each transition on two basic operations.
Especially consider the instant
when . After completing we obtain a mask in which all bits
included (bit representation of the number ), And after removing the extra bits operation will
nothing but a mask . So with a mask should be careful - if time does not stop at zero
mask, the algorithm may enter an infinite loop.
Calculate this amount. For this we note that it is nothing like the binomial theorem
expansion in the expression Ie , As required.
Primitive roots
Definition
A primitive root modulo (Primitive root modulo ) Is a number That all its powers modulo
run through all the numbers relatively prime to . Mathematically, this is formulated as follows: if
is a primitive root modulo , Then for any integer such that , There
an integer That .
In particular, in the case of a simple degree primitive roots run through all the numbers from to .
Existence
A primitive root modulo if and only if degree is either an odd prime or double prime power, and also in cases
, , .
This theorem (which has been fully proved by Gauss in 1801) is given here without proof.
From Lagrange's theorem that the index of any number modulo is a divisor . So
it is sufficient to verify that all proper divisors performed
. This is a much faster algorithm, but we can go further. Factorize number. We
prove that in the previous algorithm, it suffices to consider in
ie still among the numbers of the form there would be something for which the condition is not met, as required.
Thus, an algorithm for finding a primitive root. Find , Factorize it. Now
iterate through all the
numbers And for each consider all quantities . If the current
All these numbers were different from , It is the desired primitive root.
The running time (assuming that the number there is divisors, and erection
degree algorithm is executed Binary exponentiationIe for )
Whe
equally plus the time factorization number rein - The result, ie
value of the unknown primitive root.
About the rate of growth with the growth of primitive roots known only estimates. Known
that primitive roots - relatively small quantities. One of the famous guest - evaluation Shupe (Shoup), that, assuming the
Implementation
Function powmod () performs a binary exponentiation modulo a function generator (int p) -
is a primitive root modulo a prime (factoring of implemented here
simple algorithm for ). To adapt this function to arbitrary enough
add calculation Euler function variable phi.
where
Here is the unknown quantity So we came to the discrete logarithm problem in pure form. This problem can be
solved algorithm baby-step-giant-step Shanks for, ie, find one of
solutions this equation (or find that this equation has no solution).
Suppose we have found a solution this equation, then one of the solutions of the discrete
root will .
where chosen so that the fraction was intact. To this fraction was intact, the numerator must be a
multiple of the least common multiple and Where (recalling that the least common multiple of
This is the final convenient formula, which gives a general view of all the solutions of the discrete root.
Implementation
We present a complete implementation, including finding a primitive root, discrete logarithm
и finding and conclusion of all solutions.
int main() {
int n, k, a;
cin >> N >> k >> a;
if (a == 0) {
puts ("1 \
n0"); return
0;
}
Thus, the described algorithm is only used to sense properties of the order Not more.
Authorship algorithm apparently belongs Grice and Misra (Gries, Misra, 1978 - see references at the
end). (And, in fact, call the algorithm "Sieve of Eratosthenes" correctly: too different these two
algorithms.)
● - This means that the number of - Simple, because for it has not found any other divisors.
● - This means that the current number of - Composite, and it is a minimal prime divisor .
In both cases, the process then begins placement values in the array: we will take the number, fold And
update their value. However, our goal - to learn to do it so that in the end each number value would be
set more than once.
It is argued that this can do so. Consider the number of the form:
where the sequence - It's simple, do not exceed (Just for this purpose we need
keep a list of all primes).
All properties of this kind places a new value - Apparent power, it will be .
Why such an algorithm is correct, and why it works in linear time - see below, in the meantime let its implementation.
Implementation
Sieve is performed before said in a constant number .
This implementation can accelerate a bit, getting rid of the vector (Replacing it with a regular array with counter)
as well as getting rid of duplicate multiplying a nested loop (For which the result of work
just have to remember a particular variable).
Proof of correctness
We prove correctness algorithm, i.e. it places all the correct values, each of which
will be installed only once. This will imply that the algorithm works in linear time - since all other steps
of the algorithm obviously work for .
For this, note that any number of the only representation like this:
where - (As before) the minimum prime divisor And by has no divisors less , Ie:
Now compare this with the fact that our algorithm does - it actually for each enumerates all simple to
that it can be multiplied, ie simple to inclusive, to get the number in the specified
above view.
Therefore, the algorithm really pass for each composite number exactly once, putting him right
value .
This means the correctness of the algorithm and the fact that it runs in linear time.
length about - This seems to be inferior to the classical algorithm sieve on all counts.
However, it makes that the array, calculated by the algorithm, allows you to search any number factorization in the
segment during the order of the size of this factorization. Moreover, the price of one additional array can be made to
factorization in this division operation is not needed.
Knowing the factorization of all the numbers - very useful information for some tasks, and this algorithm is
one of the few that allow you to look for it in linear time.
Literature
● David Gries, Jayadev Misra. A Linear Sieve Algorithm for Finding Prime Numbers [1978]
BPSW test for primality
Introduction
Algorithm BPSW - this is a test number on simplicity. This algorithm is named for its inventors: Robert Bailey (Ballie), Carl
Pomerance (Pomerance), John Selfridge (Selfridge), Samuel Wagstaff (Wagstaff). Algorithm
was proposed in 1980. To date, the algorithm has not been found any counterexample, as well as the
proof has not been found.
BPSW algorithm has been tested on all numbers up to 1015. Besides, trying to find a counterexample using the PRIMO
(see [6]) Based on the simplicity of the test using elliptic curves. Program
worked for three years, did not find any counterexample, whereby Martin suggested that there is no
no BPSW-pseudosimple smaller 1010000 (pseudosimple number - a composite number for which the algorithm
produces a result "simple"). At the same time, Carl Pomerance in 1984 presented a heuristic proof that there are
infinitely many BPSW-pseudosimple numbers.
Complexity of the algorithm has BPSW O (log3 (N)) bit operations. If we compare the algorithm BPSW with other tests, such
as the Miller-Rabin test, the algorithm BPSW is usually 3-7 times slower.
The algorithm is often used in practice. Apparently, many commercial mathematical packages, wholly or
partly rely on an algorithm to check BPSW Primality.
Brief description
The algorithm has several different implementations, differing only in details. In this case, the
algorithm has the following form:
1. Run the Miller-Rabin test with base 2.
2. Run a strong Lucas-Selfridge test using Lucas sequence with parameters Selfridge.
/ /! Returns the value of k-bit number (bits are numbered from zero)
template <class T>
bool test_bit (Const T & n, unsigned k);
/ /! Multiplies a * = b (mod n)
template <class T>
void mulmod (T & a, T b, const T & n);
/ /! Computes a ^ k (mod n)
template <class T, class T2>
Tpowmod (T a, T2 k, const T & n);
/ /! Euclid's algorithm
template <class T, class T2>
Tgcd (Const T & a, const T2 & b);
Miller-Rabin test
I will not focus on the Miller-Rabin test, as it is described in many sources, including in Russian (eg see
[5]).
My only comment is that the speed of his work has O (log3 (N)) bit operations and bring the finished implementation of this algorithm:
template <class T, class T2>
bool miller_rabin (T n, T2 b)
{
return false;
Algorithm Selfridge
Among the sequences 5, -7, 9, -11, 13, ... by a first D, for which J (D, N) = -1 and gcd (D, N) = 1, where J
(x, y) - Jacobi symbol.
Parameters Selfridge will P = 1 and Q = (1 - D) / 4.
It should be noted that the parameter does not exist for Selfridge numbers that are accurate
squares. Indeed, if the number is a perfect square, then bust D comes to sqrt (N), where it appears
that gcd (D, N)> 1, ie found that the number N is composite.
In addition, the parameters will be calculated incorrectly Selfridge for even numbers and units;
however, verification of these cases is not difficult.
Thus, before the beginning of the algorithm should verify that the number N is odd, greater than 2 and
not a perfect square, otherwise (under penalty of at least one condition) should immediately withdraw
from the algorithm with the result of "composite".
Finally, we note that if D for some number N is too large, then the algorithm from a computational point of view would be inapplicable.
Although in practice this was not observed (exerted enough 4-byte number), however the likelihood of this event should not be
excluded. However, for example, in the interval [1;
106] max (D) = 47, and on the interval [1,019; 1019 +106] max (D) = 67. Furthermore, Bailey and Vagstaf 1980
analytically proved this observation (see Ribenboim, 1995/96, page 142).
U0 = 0
U1 = 1
Uk = P Uk-1 - Q Uk-2
V0 = 2
V1 = P
Vk = P Vk-1 - Q Vk-2
The converse is not generally true. Nevertheless, pseudosimple numbers in this algorithm is not very
much on what, in fact, is based algorithm Lucas.
Thus, Lucas algorithm is to calculate the UM and comparing it with zero.
Next, you need to find some way to speed up computations UK, otherwise, it is clear no practical
sense in this algorithm would not.
We have:
Uk = (ak - bk) / (a - b),
Vk = ak + bk,
First, consider the following formulas for the addition of members of Lucas sequences:
Ui + j = (Ui Vj + Uj Vi) / 2 (mod N)
Vi + j = (Vi Vj + D Ui Uj) / 2 (mod N)
Note that the division is performed in the field (mod N). These formulas are proved very
Now, having the formulas for addition and doubling sequences members Lucas and
understandable way to accelerate computing UE and VE.
Indeed, consider the binary representation of the number of E. We first result - UE and VE - equal, respectively, U1 and V1. Go
through the bits of E from younger to older, skipping only the first bit (the initial term of the sequence). For each i-th bit of U2 i will
calculate and V2 i of
previous members with the aid of doubling. Furthermore, if the current i-th bit is one, that will add to the response current U2 i and
V2 i using addition formulas. At the end of the algorithm that runs in O
(Log (E)), we have obtain the desired UE and VE.
If the UE or VE were zero (mod N), N is a prime number (or pseudosimple). If both are different from
zero, then calculate V2E, V4E, ... V2T-2E, V2T-1E. If at least one of them is comparable to zero
modulo N, the number N is prime (or pseudosimple). Otherwise, the number N is composite.
P> 0,
D = P2 - 4 * Q? 0.
Ie if D - perfect square, then the algorithm becomes almost Lucas conventional probabilistic test. One of
For example, it is possible to select the first sequence number D of 5, -7, 9, -11, 13, ... for which J (D, N) = -
1. Also suppose P = 1. Then Q = (1 - D) / 4. This method was proposed Selfridge.
However, there are other ways to select D. You can select it from the sequence 5, 9, 13, 17, 21, ... Also, let
P - the smallest odd, privoskhodyaschee sqrt (D). Then Q = (P2 - D) / 4.
It is clear that the choice of a particular method of calculating the parameters depends Lucas and its result - pseudosimple
may vary for different methods of parameter selection. As shown, the algorithm proposed by Selfridge, was very
successful all pseudosimple Lucas-Selfridge are not pseudosimple Miller-Rabin, at least, no counterexample was found.
// parameters Selfridge
T2
p = 1,
q = (p * p - dd) / 4;
// decompose n +1 = d * 2 ^ s
T n_1 = n;
+ + N_1;
T s, d;
transform_num (n_1, s, d);
/ / Algorithm Lucas
T
u = 1, v
= p, u2m
= 1, v2m
= p, qm
= q,
qm2 = q *
2, qkd =
q;
for (unsigned bit = 1, bits = bits_in_number (d); bit <bits; bit +
+)
{
mulmod (u2m, v2m, n);
mulmod (v2m, v2m, n);
while (v2m <qm2)
v2m + =
n; v2m - = qm2;
mulmod (qm, qm, n);
qm2 = qm;
redouble (qm2);
if (test_bit (d, bit))
{
T t1, t2;
t1 = u2m;
mulmod (t1, v, n);
t2 = v2m;
mulmod (t2, u, n);
T t3, t4;
t3 = v2m;
mulmod (t3, v, n);
t4 = u2m;
mulmod (t4, u, n);
mulmod (t4, (T) dd,
n);
u = t1 + t2;
if (! even
(u))
u + =
n; bisect (u);
u% = n;
v = t3 + t4;
if (! even
(v))
v + =
n; bisect (v);
v% = n;
mulmod (qkd, qm, n);
}
}
return false;
Code BPSW
Now it remains to simply combine the results of all three tests: checking for small trivial divisors, the
Miller-Rabin test, test strong Lucas-Selfridge.
Hence one can download the program (Source + Exe), containing the full implementation of the test BPSW. [77
KB]
Quick implementation
Code length can be greatly reduced at the expense of flexibility, abandoning
templates and various support functions.
be heuristically at least
2
eT 2 (1 - 3 / k) / e 2T> eT (1 - 4 / k)
for large T.
But each such n - is a counterexample to the test BPSW. Indeed, n is the number of Carmichael (ie, the number on which
the Miller-Rabin test is wrong for any reason), so it will automatically pseudosimple base 2. Since n = 3 (mod 8) and each p | n
= 3 (mod 8), it is obvious that n will pseudosimple strong base 2. Since J (5, n) = -1, then every prime p | n satisfies J (5, p) = -
1, and since the p + 1 | n +1 for every prime p | n, it follows that n - pseudosimple Lucas Lucas for any test
with discriminant 5.
2
Thus, we have shown that for any fixed k and all large T, will be at least eT (1 - 4 /
k)
counterexamples to test BPSW of numbers less than eT 2. Now, if we put x = eT 2, will be at least x1 - 4 / k
counterexamples smaller x. Since k - a random number, then our evidence indicates that
number of counterexamples, smaller x, is a number greater than x1-a for any a> 0.
Literature
Usable me literature is available online: 1. Robert Baillie;
Samuel S. Wagstaff
Lucas pseudoprimes
Math. Comp. 35 (1980) 1391-1417
mpqs.free.fr / LucasPseudoprimes.pdf
2. Daniel J. Bernstein
Distinguishing prime numbers from composite numbers: the state of the art in 2004
Math. Comp. (2004)
cr.yp.to/primetests/prime2004-20041223.pdf
3. Richard P. Brent
Primality Testing and Integer Factorisation
The Role of Mathematics in Science (1990)
wwwmaths.anu.edu.au / ~ brent/pd/rpb120.pdf
4. H. Cohen; H. W. Lenstra
Primality Testing and Jacobi Sums
Amsterdam (1984)
www.openaccess.leidenuniv.nl/bitstream/1887/2136/1/346_065.pdf
6. M. Martin
PRIMO - Primality Proving
www.ellipsa.net
7. F. Morain
Elliptic curves and primality proving
Math. Comp. 61 (203) (1993) citeseer.ist.psu.edu/rd/43190198% 2C72628% 2C1% 2C0.25%
2CDownload/ftp% 3AqSqqSqftp. inria.frqSqINRIAqSqpublicationqSqpubli-ps-gzqSqRRqSqRR-
1256.ps.gz
8. Carl Pomerance
Are there counter-examples to the Baillie-PSW primality test?
Math. Comp. (1984)
www.pseudoprime.com / dopo.pdf
9. Eric W. Weisstein
Baillie-PSW primality test
MathWorld (2005) mathworld.wolfram.com /
Baillie-PSWPrimalityTest.html
StrongLucasPseudoprime.html
Here are the implementation of several factorization algorithms, each of which individually can work as quickly or
very slowly, but together they provide a very fast method.
Descriptions of these methods are, the more that they are well described on the Internet.
// calculate a ^ M
for (size_t i = 0; i <sizeof q / sizeof q [0]; i + +)
{
T qq = q [i];
T e = (T) floor (log ((double) b) / log ((double)
qq)); T aa = powmod (a, powmod (qq, e, n), n);
if (aa == 0)
continue;
/ / If nothing found
return 1;
}
T g = 1;
for (size_t i = 0; i <primes.size () && g == 1; i + +)
{
T cur = primes
[i]; while (cur <=
n)
cur * = primes
[i]; cur / = primes [i];
b = powmod (b, cur, n);
g = gcd (abs (b-1), n);
if (g == n)
g = 1;
}
return g;
}
Method Farm
This wide method, but it can be very slow if you have small number of divisors. So run it costs only
// generate a simple 3 to m
T2 pi;
const vector <T2> & primes = get_primes (m, pi);
if (n <m * m)
return 1;
return 0;
Application
Download [5 KB] source program that uses all of these methods and test factorization BPSW for simplicity.
Fast Fourier Transform for the O (N log N).
Application to the multiplication of two
polynomials or long numbers
Here we consider an algorithm which allows to multiply two polynomials of length during ,
is much better than the time achieved trivial multiplication algorithm. Obviously,
multiplication of two long numbers can be reduced to the multiplication of polynomials, so the
two long numbers can also multiply during .
Fast Fourier Transform invention is attributed to Cooley (Coolet) and Taki (Tukey) - 1965 actually invented a FFT
repeatedly before, but its importance is not fully recognized until the appearance of
modern computers. Some researchers attribute the discovery FFT Runge (Runge) and Koenig (Konig) Finally in
1924, the discovery of this method is attributed to more Gaussian (Gauss) in 1805
Without loss of generality, we can assume that is a power of 2. When actually not a power of 2, then we
just add the missing coefficients, setting them equal to zero.
Theory of functions of a complex variable is known that the complex roots Th roots of unity
there are exactly . We denote these roots by , Then it is known that
. In addition, one of these roots (Called the principal value of the root -
th roots of unity) is that all the other roots are its powers: .
Then discrete Fourier transform (DFT) (Discrete Fourier transform, DFT) of the polynomial
(Or, equivalently, the DFT vector of coefficients) are the values of this polynomial at the
points Ie is the vector:
Thus, if the direct proceeds from the DFT coefficients of the polynomial to its values in the complex
roots - Th roots of unity, the inverse DFT - on the contrary, by the values of the coefficients of the
polynomial recovers.
But this means that if we multiply the vector and Simply multiplying each element
a vector to the corresponding element of another vector, then we get nothing else than from DFT
polynomial :
Divide it into two polynomials, one - with even and the other - with the odd coefficients:
an
Polynomials d have twice the smaller degree than the polynomial . If we can in linear time
calculated by and calculate , Then we obtain the desired algorithm
Fast Fourier Transform (as is the standard chart of "divide and conquer", and for her
known asymptotic estimate ).
For the second half of the coefficients after transformation also get a simple formula:
IFFT
So, let a vector - Values of the polynomial degrees at points .
Need to recover the coefficients polynomial. This well-known problem
called interpolationFor this task, there are some common algorithms for solving, but in this case is obtained by a
very simple algorithm (a simple fact that it does not differ from the direct FFT).
The vector can be found by multiplying the vector by the inverse matrix to the matrix, stands to the
left (which, incidentally, is called Vandermonde matrix):
Thus, we obtain:
we notice that these two problems are not real, so that the coefficients You can find such a
same algorithm "divide and rule" as a direct FFT, but instead should be used everywhere, and
each element of the result must be divided by .
Thus, the calculation of the inverse DFT is not very different from the direct computation of
the DFT, and can also be performed during the .
Implementation
Consider a simple recursive FFT implementation and IFFT sell them as one function, because the differences
between the direct and inverse FFT low. For storage complex
numbers use the standard in C + + STL type complex (defined in the header file <complex>).
In argument function takes an input vector of coefficients, and it will contain the same result. Argument shows direct or
inverse DFT should be calculated. Inside the function first checks if the length of the vector is unity, there is nothing else to
do - he is the answer. Otherwise, the vector is divided into two vectors and For which recursively calculated DFT. It
then calculates the value of ,
Starts and variable Containing the current degree of . Then calculated by DFT elements resulting
formulas described above.
If the flag is specified Then is replaced by, as each element of the result is divided by 2
(Given that these dividing by 2 occur in each level of recursion, then eventually just turns out
that all the elements on the share ).
Then the function for multiply two polynomials will be as follows:
void multiply (const vector <int> & A, const vector <int> & B, vector
<int> & res) {
vector <base> fa (a.begin(),
A.end()), fb (b.begin(), B.end());
size_t n = 1;
while (n <max (a.size(), b.size())) n << = 1;
n << = 1;
fa.resize (n), Fb.resize (n);
fft (fa, false), Fft (fb,
false);for (size_t i =0; i <n; + +
I)
fa[i] * =
Fb[i]; fft (fa, true);
res.resize (n);
for (size_t i =0; i <n; + + I)
res[i] = int (fa[i]. Real() + 0.5);
}
This function works with polynomials with integer coefficients (although, of course, theoretically nothing prevents her
work with fractional coefficients). However, it appears the problem of a large error in the DFT: error can be significant, so
better to round the number the surest way - by adding 0.5 and then rounding down.
Finally, the function for multiplying two long numbers practically does not differ from the function for
multiplying polynomials. The only feature - that after the multiplication of numbers as they should normalize
polynomials, ie perform all the carries bits:
int carry = 0;
for (size_t i =0; i <n; + +
I) { res[i] + =
Carry; carry =
res[i] / 10; res[i]
% = 10;
}
(Since the length of the product of two numbers never exceed the total length of numbers, the size
of the vector enough to perform all the carries.)
Improved execution: computing "on the spot" without
additional memory
To increase the efficiency abandon recursion explicitly. In the above recursive implementation, we clearly
separated vector two vector - elements on even positions attributed to the same time creates a vector, and on the
odd - to another. However, if we reorder elements in a certain way, the need for creating temporary vectors would
then be eliminated (ie, all the calculations we could produce "on the spot" right in the vector ).
Note that the first level of recursion elements, junior (first) position bits are zero, refer to the vector And the least
significant bits positions which are equal to one - to the vector . On the second level of recursion is done the same thing,
but for the second bit, etc. So if we are in a position each element
invert the order of the bits, and reorder elements of the array in accordance with the new indices, we
and obtain the desired order (it is called bitwise inverse permutation (Bit-reversal
permutation)).
For example, for this order is as follows:
Indeed, on the first level of recursion (surrounded by curly braces) conventional recursive
algorithm is a division of the vector into two parts: and. As we see, in
bitwise inverse permutation of this vector corresponds to a division into two halves: the first element and the last
element. Then there is a recursive call of each half;
let the resulting DFT from each of them had returned to the place of the elements themselves
(ie in the first and second halves of the vector , respectively):
Now we need to merge the two into one DFT for the vector. But the elements got so well that the union can be performed
directly in the array. Indeed, we take the elements and is applicable to
These butterfly transformation, and the result put in their place - and this place and would thus that
it should have received:
Similarly, we apply the transformation to a butterfly and and the result is put in their place, etc. As a result, we obtain:
Initially, a vector bitwise inverse permutation is applied, which calculates the amount of significant
)
bit ( Including , And for each item is the corresponding position, which bit write
have a bit representation of the number Written in reverse order. If the resulting position was as a result
of more , The elements in these two positions should be exchanged (unless this condition, each pair will
exchange twice and in the end nothing happens.)
) Are calculated for the
Then, the algorithm steps, on the second of which ( DFT
block length. For all of these units will be the same value of a primitive root , And is
is stored in a variable . Loop iterates on the blocks, and invested in it by cycle
applies the transformation to all elements of the butterfly unit.
You can further optimization of reverse bits. In the previous implementation, we explicitly took
over all bits of the number, incidentally bitwise inverted order number. However, reverse bits can
be performed differently.
For example, suppose that - already counted the number equal to the number of bits reverse permutation .
Then, during the transition to the next number we must add one to the number, but add it to a
"Inverted" notation. In a conventional binary system to add one - so remove all units standing at the end of the number (ie, younger
group of units), and put the unit in front of them. Accordingly, the "inverted" system, we have to go through the bits of the number,
starting with the oldest, and while there are ones, delete them and move on to the next bit; when will meet the first zero bit, put him in a
unit and stop.
Additional optimization
Here are a list of other optimizations, which together can significantly speed up the above
"improved" implementation:
● Predposchitat reversed bits for all numbers in a global table. It is especially easy, when the size
of for all calls the same.
This optimization becomes noticeable when a large number of calls. However, its effect can be
even notice when three calls (three calls - the most common situation, ie when it is required once
to multiply two polynomials).
● Abandon the use of (go to normal arrays).
The effect of this depends upon the particular compiler, but is typically present and accounts for approximately 10% -20%.
● Predposchitat all power number. In fact, in this cycle algorithm repeatedly made through all the
powers of from to :
Accordingly, before this cycle we can predposchitat from an array all the required extent, and
thus get rid of unnecessary multiplications in a nested loop.
Tentative acceleration - 5-10%.
● Get rid of array accesses Index, Instead use pointers to the current array elements, advancing
them to the right one at each iteration.
At first glance, optimizing compilers should be able to cope with this, but
in practice it appears that the replacement of references to
arrays and pointers
program accelerates common compilers. Profit is 5-10%.
● Abandon the standard type of complex numbers Rewriting it to
own implementation.
Again, this may seem surprising, but even in modern compilers benefit from such rewriting can be up to
several tens of percent! This indirectly confirms a widespread assertion that compilers perform worse
with sample data types, optimizing work with them much worse than non-formulaic types.
● Another useful optimization is clipping lengthWhen the length of the working unit becomes small (say,
4), to calculate the DFT for it "manually". If the paint in these cases explicit formulas with a length equal
to, the values of sines, cosines take integer values, whereby it is possible to get a speed boost for a few
tens of percent.
Let us here describe improved realization with (except the last two items which lead to overgrowth of
code):
int rev[MAXN]; base
wlen_pw[MAXN];
void fft (base a[], int n, bool invert) {
for (int i =0; i <n; + + I)
if (i <rev[i])
swap (a[i], A[rev[i]]);
if (invert)
for (int i =0; i <n; +
+ I) a[i] / =
N;
}
On common compilers faster than the previous implementation of this "improved" version of 2-3.
All the rest roots Th roots of unity in modulus You can get your degree primitive root
(As in the complex case).
For use in the fast Fourier transform algorithm we needed to root primivny existed for some , A power of two, and all the lesser
degrees. And if in the complex case, there was a primitive root for any , In the case of the modular arithmetic is generally not
so. However, note that if Ie -Th power of two, the modulo we have:
Here, the function is the inverse of the element modulo (Cf. Inverse element in a field
module). Constants , specify the module and a primitive root, and - Inverse
element modulo .
As practice shows, the implementation of integral DFT works even slower implementation of complex numbers
(due to the sheer number of transactions taking absolute value), but it has advantages such as low memory
usage and the lack of rounding errors.
Some applications
In addition to direct application for multiplying polynomials or long numbers, we describe here
some other applications of the discrete Fourier transform.
Two strips
Two strips are defined as two Boolean (ie numerical values 0 or 1) and the array. Required
find all such positions in the first strip that if you apply, since this position, the second strip in any place will
not work immediately on both strips. This problem can be reformulated as follows: given a map strips
as 0/1 - you can get up into the cell or not, and given some figure as a template (as an array, where 0 - no
cells, 1 - yes), requires Find all positions in the strip which can be attached figure.
This problem is actually no different from the previous problem - the problem of the scalar product. Indeed, the dot
product of two 0/1 arrays - the number of elements in which both units were. Our task is to find all cyclic shifts of the
second strip so that there was not a single item, which would be in both strips were unity. Ie we have to find all cyclic
shifts of the second array, in which the scalar product is zero.
The algorithm works for Wherein - The number of vertices - The number of edges.
Implementation
Implement the above algorithm in the
language C + +. Input:
/ / Read graph
...
Circumvention itself:
queue <int>
Q; q.push
(s);
vector <bool> Used (n);
vector <int> D (n), P
(n); used[s] = true;
p[s] = -1;
while (! Q.empty()) {
int v = q.front();
q.pop();
for (size_t i =0; i <g[v]. Size(); + +
I) { int to = g[v] [i];
if (! Used[to]) {
used[to] = true;
q.push (to);
d[to] = D[v] + 1;
p[to] = V;
}
}
}
If we now want to restore and display the shortest path to some vertex This can be done
as follows:
if (! Used[to])
cout << "No path!";
else {
vector <int> Path;
for (int v = to; v! = -1; v =
p[v]) path.push_back
(v);
reverse (path.begin(),
Path.end());cout << "Path:";
for (size_t i =0; i <path.size(); +
+ I) cout << Path[i] + 1 <<
"";
}
● Finding the shortest path in 0-1-column (Ie, weighted graph, but with weights equal only 0 or 1) is sufficient to slightly
modify the breadth-first search, if the current edge of zero weight, and an improvement of the distance to some
vertex, then this vertex does not add to the end, and of the queue.
● Location shortest cycle in an undirected unweighted graph: search width of each vertex; once during the traversal we try
to go from the current vertex on some edge in the already visited vertices, then it means that we have found the shortest
cycle and stop crawling in width; found among all such cycles (one from each run bypass) choose the shortest.
● Find all edges that lie on any shortest path between a given pair of vertices .
And
You need to run two breadth-first search: from of . We denote array shortest
distances obtained from the first bypass and through - As a result of the second passage. Now for
any edge easy to check whether it is in any shortest path: the criterion will
condition .
● Find all vertices lying on any shortest path between a given pair of vertices .
And
You need to run two breadth-first search: from of . We denote array shortest
distances obtained from the first bypass and through - As a result of the second passage. Now for
any vertex easy to check whether it is in any shortest path: the criterion will
condition .
● Find even the shortest path in the graph (ie, a path of even length). For this we need to build
auxiliary graph, whose vertices are states Where - number of current peaks
- The current parity. Any edge original graph in this new column will turn into two
ribs and . After that, on this graph should preorder traversal to find the shortest
path from the start vertex to the terminal, with parity equal to 0.
Problem in online judges
List of tasks that can be taken using a wide detour: ● SGU # 213
Implementation
vector <int> time_in, time_out; / / "Time" entry and exit from the top
int dfs_timer = 0; / / "Switch" to determine the time
void dfs (int v) {
time_in [v] = dfs_timer +
+; color [v] = 1;
for (vector <int> :: iterator i = g [v]. begin (); i! = g [v].
end (); + + i) if (color [* i] == 0)
dfs (* i);
color [v] = 2;
time_out [v] = dfs_timer + +;
}
This is the most common code. In many cases, the time of entry and exit from the top is not important, as well as the
vertex colors are not important (but then you have to enter the same within the meaning of the boolean array used). Here
are the most
simple implementation:
Algorithm
Solutions for use traversal depth.
Assume that the graph is acyclic, ie solution exists. What makes a detour into the depths? When you run out of some vertex
he tries to run along all edges emanating from . Along the edges, the ends of which have already been visited before, it does
not pass, and along all the others - and calls itself runs on their ends.
Thus, by the time the call all vertices reachable from both directly (by
one edge) and indirectly (by the way) - all such vertices already visited bypass. Consequently, if we are at the moment out of
our top add to the beginning of a list, then in the end of this list
will topological sorting.
These explanations can be presented in a slightly different light, using the concept of "Release date" traversal depth.
Retention time for each vertex - This is the point in time at which the call has finished work
traversal depth of it (retention times can be numbered from to ). Easy to understand that in a preorder traversal
depth of the escape from some vertex always greater than the output of all the vertices reachable from it
(Because they have been visited or to call Or during it.) Thus, the desired
topological sorting - sorting in descending order of retention times.
Implementation
Show implementation, which implies that the graph is acyclic, ie desired topological sort exists. If necessary check on the acyclic
graph easily inserted into the bypass in depth, as described in Article circumvention
в depth.
void topological_sort() {
for (int i =0; i <n; + +
I) used[i] =
false;
ans.clear();
for (int i =0; i <n; +
+ I) if (!
Used[i])
dfs (i);
reverse (ans.begin(), Ans.end());
}
Here the constant should be set equal to the maximum possible number of vertices in the graph.
The main function of a solution - it topological_sort, it initializes tagging crawl deep, runs it, and the result
is a response vector .
Implementation
To implement a little more comfortable traversal depth:
int n;
vector <int>
G[MAXN];bool
used[MAXN]; vector
<int> Comp;
void dfs (int v) {
used[v] = true;
comp.push_back (v);
for (size_t i =0; i <g[v]. Size(); + +
I) { int to = g[v] [i];
if (! used[to])
dfs (to);
}
}
void find_comps() {
for (int i =0; i <n; + +
I) used[i] =
false;
for (int i =0; i <n; + +
I) if (!
used[i]) {
comp.clear();
dfs (i);
cout << "Component:";
for (size_t j =0; j <comp.size(); +
+ J) cout << '' << Comp[j];
cout << Endl;
}
}
The main function to call - It finds and displays the components of the graph.
We believe that given the graph adjacency lists, ie contains a list of vertices that have edges from the top
. Constanta should be set equal to the maximum possible number of vertices in the graph.
where the
symbol hereinafter we denote reachability, ie existence of a path from the first vertex
in the
second.
It is clear that for strongly connected components of the graph do not intersect, ie actually this partition
all vertices of the graph. Hence the logical definition condensation as a graph resulting from this
Count each contraction strongly connected components in a single vertex. Each vertex of condensation
an
corresponds to a strong component of the graph And a directed edge between two vertices d
graph condensation held if a pair of vertices Between which there was an edge
in the original graph, ie .
The most important property of the graph condensation is that it acyclic. Indeed, suppose
that , Prove that . From the definition of condensation we get that there are two peaks
and That . Will prove the opposite, ie assume , Then there are two
. But an
vertices and That since d are in the same strongly connected component, the
and. As a result, combining the way, we find
between them is the way; similarly for that And simultaneously
. Consequently, must belong to the same strongly connected component, ie received
contradiction, as required.
Algorithm described later in this column identifies all strongly connected components. Count on
them to build condensation is not difficult.
Algorithm
Algorithm described here has been proposed independently Kosarayu (Kosaraju) and sarira (Sharir) in 1979 It is very easy to
implement algorithm based on two series dfsAnd because working for
time .
In a first step algorithm, a series of detours in depth attending the entire graph. For this We have passed through all the
vertices of the graph and each vertex not yet visited call traversal depth. At
Moreover, for each vertex remember Release time . These retention times play a key role
algorithm, and this role is expressed in the following theorem.
First, we introduce the notation: time to of strongly connected components is defined as the maximum
values of all . Moreover, in the proof will be referred to the time of entry and
each vertex And similarly define the time of entry for each component of a strong
connection of at least the quantities all .
Theorem. Let - Two different strongly connected components, and let the graph condensation between
them an edge . Then .
The proof there are two fundamentally different cases depending on which of the component
first tour will go in depth, ie depending on the ratio between and :
● The first part has been achieved. This means that at some time in the crawl depth comes
a
n not yet been visited.
in a vertex components with all other components of the vertices d However, since
by hypothesis, the graph has an edge
condensations , From the top will be achieved not only the entire component
but the whole
component . This means that when you start from the top in depth tour will pass through all
and, and, therefore, they will be in relation to the descendants in the tree traversal in depth,
component tops ie
for each vertex will be performed , QED
● The first component was achieved . Again, at some time in the crawl depth comes in
an
some vertex , And all the other vertices of the component d are visited. Since by hypothesis
condensations in the graph there rib , Is due to condensation acyclic graph, not
there is a way back Ie bypassing the depth from the top reaches heights. This means that
they will be visited in the traversal depth later, whence , QED
The above theorem is basis for the algorithm Search Strongly Connected Components. From this it follows
condensations in the graph comes from components with greater a component
that any edge magnitude with
smaller size.
If we sort all vertices in descending order of release time , It would be the first
some vertex , Owned by "root" strongly connected components, ie which excludes none of the edges in the graph
condensations. Now we would like to start a tour of this vertex That would only visited this component strongly connected and
has not gone to any other; learning how to do it, we can gradually select all strongly connected components: removing the first
vertex of the graph of the selected component, we again find among the remaining vertex with the highest value Again run
out of it this tour, etc.
To learn how to do this tour, consider transposed graph Ie graph obtained from
changing the direction of each edge to the opposite. It is easy to understand that in this graph are those
same strongly connected components as in the original graph. Moreover, the graph condensation for a
will be equal transposed graph condensation of the original graph. This means that now
of the considered "root" component will not be leaving the edges in other components.
So, to get around the whole "root" strongly connected components containing a vertex
Enough to run from the top of bypassing in the graph. This tour will visit all the vertices of this component
strong connectivity and only them. As already mentioned, then we can mentally remove these vertices of the graph, find the
next vertex with the maximum value and run circumvention transposed graph
therefrom, etc.
Thus we have constructed the following algorithm allocation Strongly Connected Components:
Step 1. Launch a series of detours in depth graph, which returns the top in order of increasing time
Release Ie some list .
Step 2. Build transposed graph . Launch a series of detours in depth / width of the graph in
order determined by list (Namely, in the reverse order, ie in descending order of time
exit). Each set of vertices reached as a result of the next crawling run and there will be another
strongly connected components.
Asymptotics algorithm clear equal Because it represents only two round trips
depth / width.
Finally, it is appropriate to note the connection with the concept of topological sorting. First, step 1
algorithm is neither more nor less than a topological sort of the graph (in fact, this means sorting peaks retention
time). Secondly, the chart is that the strongly connected components, and it generates in order to reduce their
retention times, so it generates components - the vertices of condensation in topological sort order.
Implementation
int main() {
int n;
Reading ... n ...
for (,;) {
int a, b;
Reading ... the next edge (a, b) ...
g[a]. Push_back (b);
gr[b]. Push_back
(a);
}
used.assign (n,
false);for (int i =0; i
<n; + + I)
if (! Used[i])
dfs1 (i);
used.assign (n, false);
for (int i =0; i <n; + + I) {
int v = order[n-1-I];
if (! Used[v]) {
dfs2 (v);
Conclusion ... the next component ...
component.clear();
}
}
}
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
● M. Sharir. A strong-connectivity algorithm and its applications in data-flow analysis [1979]
Search bridges
Suppose we are given an undirected graph. A bridge is an edge, whose removal makes the graph disconnected (or, rather,
increases the number of connected components). You want to find all the bridges in a given graph.
Informally, this problem is formulated as follows: it is required to find the road map given all the "important" roads, ie such way
that removing either of them will lead to the disappearance of the path between any pair of cities.
Algorithm
Run traversal depth from an arbitrary vertex of the graph; denoted by . Note the following
fact (Which is not hard to prove):
● Suppose we are bypassed in depth, looking now all edges from the top . Then, if the current
edge such that the tops of and any of its descendants in the tree traversal depth of no return edges
to the top or any of its ancestor, then this edge is a bridge. Otherwise, it is not a bridge.
(In fact, this condition we check if there are other ways of in But to descend along the edge
Tree traversal in depth.)
It now remains to learn how to check this fact for each vertex effectively. For this we use
"Time of entry into the top", computed depth-first search algorithm.
So let - this time call DFS at the top . Now we introduce an array Which
and allow us to answer the above questions. Time equal to the minimum time of entry into the very
top , Time of call in each vertex That is the end of a reverse edge And
Also all values of for each vertex , Which is a direct son in the search tree:
(Here "back edge" - the opposite edge, "tree edge" - edge of the tree)
Then, from the top or her offspring have the opposite edge in its parent if and only if there is such a son
That . (When , It means that there is the opposite edge,
coming exactly ; if , It means that there is an edge in the inverse
ancestor vertices.)
Thus, if the current edge (Belonging to the tree search) is performed ,
then this edge is a bridge; otherwise it is not a bridge.
Implementation
If we talk about the actual implementation, here we must be able to distinguish three cases: when
we go on the edge of the tree depth-first search, when we go to the opposite edge, and when
trying to go along the edge of the tree in the opposite direction. It is, accordingly, the cases:
● - Edges of the tree search criterion;
● - Criterion reverse rib;
●
- Criterion of passage along the edge of the search tree in the opposite
direction.
Thus, for the implementation of these criteria, we need to pass in the search function in the top of the
depth of the current node ancestor.
void find_bridges() {
timer = 0;
for (int i =0; i <n; + +
I) used[i] =
false;
for (int i =0; i <n; +
+ I) if (!
Used[i])
dfs (i);
}
Here, the main function to call - it - it produces the necessary initialization and start crawling in
depth for each connected component of the graph.
In this case - it's a function that will respond to the fact that the edge is a bridge, for
example, show this to the screen edge.
Constanta in the beginning of the code should be set equal to the maximum possible number
of vertices in the input graph.
It should be noted that this implementation does not work correctly in the presence of the column multiple edgesShe actually
does not pay attention, fold edge or whether it is unique. Of course, multiple edges should not be included in
response, so calling You can check further, not a multiple of whether we want to add an edge
not top of the ancestor, and
response. Another way - more accurate work with the ancestors, ie transfer to the number
ribs, which we entered the top (you will need to additionally store rooms all edges).
Algorithm
Start the tour in depth from an arbitrary vertex of the graph; denoted by . Note the
following fact (Which is not hard to prove):
● Suppose we are bypassed in depth, looking now all edges from the top . Then, if
current edge is that from the top and from any of its descendant tree traversal depth no inverse
ribs or any ancestor vertices , Then the vertex is an articulation point. Otherwise, i.e. if bypass in depth
through all the tops of the ribs , And found satisfying the above conditions the edge, then the vertex is
not an articulation point. (In fact, this condition we check if there are other ways of in )
● We now consider the remaining case: . Then this vertex is an articulation point if and only if this node has more than one son
in the tree traversal in depth. (In fact, this means that the passing of the for an arbitrary edge, we could not get around the whole
graph, which implies that - Point of articulation).
(Compare the wording with the wording of this criterion to criterion search algorithm bridges.)
It now remains to learn how to check this fact for each vertex effectively. For this we use the "time of
entry into the top", computed depth-first search algorithm.
. Now we introduce an
So, let - This time call DFS at the top array Which
equal to the minimum time of entry into the
and allow us to answer the above questions. Time very
An
top , Time of call every vertex is the end of a reverse edge d
Also all values of for each vertex , Which is a direct son in the search tree:
(Here "back edge" - the opposite edge, "tree edge" - edge of the tree)
Then, from the top or her offspring have the opposite edge in its parent if and only if there is such a
son That .
Thus, if the current edge (Belonging to the tree search) is performed ,
then the vertex is an articulation point. For the initial vertex another criterion: for this vertex
need to count the number of immediate sons in the tree traversal in depth.
Implementation
If we talk about the actual implementation, here we must be able to distinguish three cases: when
we go on the edge of the tree depth-first search, when we go to the opposite edge, and when
trying to go along the edge of the tree in the opposite direction. It is, accordingly, the cases,
and . Thus, we need to pass in the search function in the top of the depth of the current node ancestor.
vector <int>
G[MAXN];bool
used[MAXN];
int timer, tin[MAXN], Fup[MAXN];
int main() {
int n;
Reading ... n and g ...
timer = 0;
for (int i =0; i <n; + +
I) used[i] =
false;
dfs (0);
}
Here the constant must be set equal to the maximum possible number of vertices in
the input graph.
Function code - is a function that is responsive to the fact that the top
is an articulation point, for example, to display this on the top of the screen (it should be borne in
mind that for the same vertex, this function can be called multiple times.)
Informally, this problem is formulated as follows: it is required to find the road map given all the "important" roads, ie such way
that removing either of them will lead to the disappearance of the path between any pair of cities.
Algorithm described here is onlineThat means that the count was not input in advance is known
and whose edges are added to it one by one, and after each addition of the algorithm recalculates
all bridges in the current column. In other words, the algorithm is designed to work effectively in a
dynamic, changing graph.
More precisely, formulation of the problem Next. Initially empty graph and consists of vertices. Then receives
requests, each of which - a pair of vertices that represent an edge is added to the
graph. Required after each request, i.e. after the addition of each edge, display the current number of
bridges in the graph. (You may want to keep a list of all edges, bridges, as well as explicit support rib
doubly connected components.)
Algorithm described below works in time where - The number of requests. The algorithm is based
on data structure "A system of disjoint sets".
Present implementation of the algorithm, however, works in time, because it uses a simplified version of
one place system of disjoint sets rank without heuristics.
Algorithm
It is known that edge-bridges divide vertices into components called costal doubly connected components. If
each component costal doubly connected to squeeze into a single vertex, and leave only the edges of bridges
between these components, you get an acyclic graph, ie forest.
Algorithm described below support this explicitly forest component costal doubly connected.
It is clear that initially, when the graph is empty, it contains costal doubly connected component
unrelated to each other in any way.
When you add the next edge three situations may occur:
● Both ends and are in the same component costal doubly connected - then this edge is not a bridge, and
does not alter the structure of the forest, so just skip this edge.
Thus, in this case, is not changed by the bridges.
● Vertices and are in different connected components, ie connect two trees. In this case, the rib
becomes the new bridge, and the two trees are merged into one (and all the old bridges remain). Thus, in
this case, the number of bridges is incremented.
● Vertices and are in the same component, but in different components of the costal doubly connected. In this
case, the rib forms a ring together with some of the old bridges. All of these bridges are no longer bridges, and the
resulting cycle must be combined into a new component costal doubly connected.
Thus, in this case, the number of bridges is reduced by two or more.
Consequently, the whole problem is reduced to the effective implementation of all these operations over the forest
component.
● Checking whether the two are listed in the top of the same component / doubly connected.
Typically, a query is made to the structure "A system of disjoint sets."
● Joining two trees in one for some edge . As it could happen that no vertex Or vertex are not roots of their trees,
the only way to connect these two trees
-perepodvesit one of them. For example, you can perepodvesit single tree on the vertex And then
attach it to another tree, making the top subsidiary to .
However, there is a question about the effectiveness of the operation perepodveshivaniya: to perepodvesit tree rooted at
in the opposite direction, as well as changing
at the top, you have to pass on the way in from redirecting pointers the reference
ancestor on the system of disjoint sets responsible for the connected components.
Whe
Thus, the cost of the operation has perepodveshivaniya rein - Height of the tree. You can evaluate it
even higher, saying that this is the value Wherein - The number of vertices in the tree.
We now apply a standard method: we say that two trees perepodveshivat will then wherein the lower vertices. Then
intuitively, that the worst case - when you combine two tree of approximately equal size, but then the result is twice the size
tree that does not allow such a situation to occur many times. Formally, this can be written as the recurrence relation:
where by we denote the number of operations required to obtain wood from using vertices
perepodveshivaniya operations and merging trees. This is a known recurrence, and it has
decision .
Thus, the total time spent on all perepodveshivaniya, make If we
will always be the lesser of two perepodveshivat tree.
We have to maintain the size of each connected component, but the data structure "system
disjoint sets "lets you do this easily.
● Find a cycleFormed by adding a new edge in a tree. Practically, this means that
we need to find the lowest common ancestor (LCA) of vertices and .
Note that then we compress all of the vertices in one cycle of the detected peak, so we want any of the
search algorithm LCA, the running time of the order of its length.
Since all the information about the structure of the tree that we have - this links on the ancestors, the
seems the only possible next search algorithm LCA: mark the top and as
visited, then go to their ancestors and and mark them, then to their ancestors, and so on, until
does not happen, that at least one of the two current peaks is already marked. This will mean that the
current peak - is the desired LCA, and it will be necessary to repeat the path again to her from the top
and from the top - thus we find the desired cycle.
Obviously, this algorithm works in time order of the length of the desired cycle, since each of the two
pointers could not pass a distance greater than this length.
Implementation
We present here the final implementation of the whole algorithm.
For simplicity, a system of disjoint sets for doubly connected component is written without rank
heuristics, therefore the total amount on the asymptotic behavior of the average request. (The fact
how to achieve the asymptotics , Described above in paragraph "compression cycle.")
Also in this embodiment, the ribs themselves are not stored bridges, and kept only their number - see variable
. However, if desired, will have no difficulty to start All of the bridges.
Initially, you must call the function that initializes the two systems of disjoint
sets (assigning each vertex in a separate set, and affixing a size equal to one), marks the ancestors
.
The main function - it That handles the request to add a new edge.
Constanta should be set equal to the maximum possible number of vertices in the input graph.
void init() {
for (int i =0; i <n; + + I)
{ bl[i] = Comp[i] =
I; size[i] = 1;
par[i] = -1;
}
bridges = 0;
}
a = par[a];
}
if (b! = -1) {
b = get(b);
vb.pb (b);
if (u[b] == Cu) {
lca = b;
break;
}
u[b] = Cu;
b = par[b];
}
}
Algorithm
Here we describe an algorithm, which offered a Danish researcher Dijkstra (Dijkstra) in 1959
Head Array In which each vertex will store the current length the shortest path from a
. Initially And for all other vertices, this length is equal to infinity (when implemented on
computer usually as infinity just choose sufficiently large number, certainly more possible path
length):
Furthermore, for each vertex will be stored, labeled it yet or not, ie 's have a boolean array
. Initially, all vertices are labeled, ie
Dijkstra algorithm itself consists of iterations. On the next iteration selected vertex with the
lowest value of not yet labeled, ie:
(It is understood that on the first iteration will be selected starting vertex.)
Selected so the top notes marked. Further, in the current iteration of the vertices
produced relaxation: Browse all edges Emanating from the vertex, and for each
such a vertex the algorithm tries to improve the value of . Let the length of this edge is equal , Then
relaxation as a code looks like:
At the ends the current iteration, the algorithm proceeds to the next iteration (again selected vertex with the
smallest value, the relaxation produced therefrom, etc.). In the end, after iterations, all vertices will be labeled,
and the algorithm completes its work. It is argued that the values found is the required length of the shortest
paths from in .
It is worth noting that if not all the vertices reachable from the top , The value for them and
remain endless. It is clear that the last few iterations of the algorithm will just choose these peaks, but no useful work
to produce these iterations will not (because an infinite distance can not prorelaksirovat others, even too infinite
distance). Therefore, the algorithm can be immediately stopped as soon as the selected vertices taken top with infinite
distance.
Restoration paths. Of course, usually need to know not only the length of the shortest paths but also to obtain own way. We
show how to preserve enough information to restore the shortest path from
to any vertex. It's enough to so-called array ancestors: Array Wherein for
each vertex node number is stored , Which is the penultimate in the fast track to the top
. Here we use the fact that if we take the shortest path to some vertex And then remove from
last vertex of this path, we get the path ending with a vertex And this way
is the shortest for the top . So if we have this array of ancestors, the shortest path
can be restored to him, just every time taking the ancestor of the current node, until we come to the starting vertex - So we
Necessary to understand how to build the array ancestors. However, this is very simple: at every
successful relaxation, ie when the selected vertices an improvement of the distance to a vertex We
write that ancestor vertices is the peak :
Proof
The main assertion, based on which the correctness of Dijkstra's algorithm, following. Allegedly that after some
vertex becomes marked, the current distance to it already
is the shortest, and therefore more will not change.
Proof will produce by induction. For the first iteration of the justice of his obvious - for the top have, which is
the length of the shortest path to it. Suppose now that
statement holds for all previous iterations, ie all already labeled vertices; prove that it is not disturbed after the current iteration.
Let - Vertex selected in the current iteration, ie
vertex, which is going to mark the algorithm. We prove that indeed equal to the length of the shortest path to the
She (we denote this length by ).
Consider the shortest path to the top. Clearly, this path can be divided into two paths: consisting only
of labeled vertices (at least the starting vertex is in the way) and the rest of the way (She also
may include marked the top, but certainly begins with untagged). We denote first
top path and through - the last point of the path.
. However,
We first prove the assertion for the top, ie prove the equality this
almost clear: in fact on one of the previous iterations, we chose the top and perform the relaxation of it. Since (by virtue of the
choice of the vertex ) Shortest path to is the shortest path to plus edge , Then if the relaxation of value really
set to the desired value.
Due to the non-negativity values of edges of the shortest path length (And she just proved
) Does not exceed the (After
equal length of the shortest path to the top. Given that all,
Dijkstra's algorithm could not find a shorter route than it is at all possible), we finally obtain the relation:
On the other hand, since both And - Unmarked vertex, then, since in the current iteration been
chosen vertex And not the top , Then we get another inequality:
From these two inequalities we conclude equality And then found out before relations and obtain:
QED.
Implementation
Thus, Dijkstra's algorithm is iterations, each of which is selected unlabeled vertex with the lowest value, the vertex is
marked, and then iterates through all the edges emanating from
Current top and along each edge is an attempt to improve the value the other end of the
rib. Time of the algorithm consists of:
● Just search the top with the lowest value among all unmarked vertices, ie of vertices
● times tries relaxations
At the simplest implementation of these operations on the top search operations will be spent, and
One relaxation - operations, and the final asymptotics algorithm is:
Implementatio
n:
const int INF = 1000000000;
int main() {
int n;
Reading ... n ...
vector <vector <pair <int,int>>> G (n);
Graph reading ... ...
int s = ...; / / top of page
Here the graph is stored in the form of adjacency lists: for each vertex list contains a list of edges,
Where the first element of the pair - the top,
originating from this node, i.e. a list of pairs in
which an edge, and the second element - the weight of
the edge.
After reading infest arrays distances , Labels and ancestors . Then iterates. On
each iteration of the first peak is Having the smallest distance of unlabelled vertices.
If the distance to the selected vertex is equal to infinity, then the algorithm stops. Otherwise, the vertex is marked as
tagged, and scans all edges emanating from a given vertex, and
run along each edge relaxation. If relaxation is successful (ie, the distance changes)
then recalculated distance and stored ancestor .
After all the iterations in the array are the length of the shortest paths to all vertices in the array and
- The ancestors of all vertices (except the starting ). Restore the path to any vertex as follows:
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
● Edsger Dijkstra. A note on two problems in connexion with graphs [1959]
Finding the shortest paths from a given
vertex to all other vertices of Dijkstra
algorithm for sparse graphs
Formulation of the problem, the algorithm and its proof, see article about the general Dijkstra.
Algorithm
Recall that the complexity of Dijkstra's algorithm consists of two basic operations: Time Spent tops with the lowest value of the
At the simplest implementation, these operations require correspondingly and time. Given that
times, and the , We obtain the asymptotic behavior of the
first operation just performed second - simplest
implementation of Dijkstra's
algorithm: .
It is clear that this asymptotic behavior is optimal for dense graphs, ie when . The more rarefied
graph (i.e., less than compared to the maximum number of edges ), The less becomes the optimal
this estimate, and the fault of the first term. Thus, it is necessary to improve the operating times of the
first type is not greatly deteriorating the run-time operations of the second type.
To do this, use a variety of auxiliary data structures. The most attractive An
are Fibonacci heapWhich allow for the operation of the first kind d
second - for . Therefore, when using Fibonacci heaps time Dijkstra algorithm
be That is almost the theoretical minimum for the search algorithm
the shortest path. Incidentally, this estimate is optimal for algorithms based on Dijkstra's algorithm, ie Fibonacci heaps are optimal from
this point of view (this is an optimality actually based on the impossibility of the existence of such an "ideal" data structure - if it existed, it
would be possible to sort in linear time, which is known in the general case is impossible;
however, it is interesting that there is an algorithm Torup (Thorup), who is looking for the shortest path
to the optimal linear, asymptotic behavior, but it is based on a very different idea than Dijkstra's
algorithm, so no contradiction here.) However, Fibonacci heaps are quite complicated to implement
(and, it should be noted, have considerable constant hidden in the asymptotic behavior).
As a compromise, you can use the data structure to allow you to Both types
operations (In fact, this extract and update the minimum element) for . Then the time
Dijkstra's algorithm will be:
Implementation
set
Let's start with the container . Because we need to keep the container top, ordered according to their values, it is
convenient to place the container in pairs: the first element of the pair - the distance, and the second - the number of vertices.
As a result,
в pair will be stored automatically sorted by the distance that we need.
int main() {
int n;
Reading ... n ...
vector <vector <pair <int,int>>> G (n);
Graph reading ... ...
int s = ...; / / top of page
Unlike conventional Dijkstra algorithm becomes unnecessary array. Its role as a function
finding vertices with the smallest distance, performs . Initially, he put the starting vertex with its distance. The
main loop of the algorithm is executed in a queue until there is at least one vertex. Is retrieved from the queue with the
smallest distance to the vertex, and then executed from its relaxation. Before performing each successful relaxation
we first remove from old pair and then, after the relaxation back to attach a new pair (new Proximity ).
priority_queue
Fundamentally different from here No, except for the moment that any elements removed from the impossible
(although theoretically heap support such an operation,
in the standard library is not implemented). Therefore it is necessary to make "workaround": the relaxation simply will not
remove the old pair from the queue. As a result, the queue can be simultaneously several pairs of the same vertices (but
at different distances). Among these pairs we are interested in only one, for which the element equal, and all
others are bogus. So
necessary to make a slight modification: the beginning of each iteration, when we learn from the next couple of turns, will
check fictitious or not (it is enough to compare u). It should be noted that
an important modification: if not make it, it will lead to a significant deterioration of the asymptotics (up ).
Yet it must be remembered that arranges elements in descending order and not ascending,
as usual. The easiest way to overcome this feature is not an indication of its comparison operator, but simply as an element of
putting distance with a minus sign. As a result, the root elements of the heap will be provided
с the smallest distance that we need.
int main() {
int n;
Reading ... n ...
vector <vector <pair <int,int>>> G (n);
Graph reading ... ...
int s = ...; / / top of page
Implementation
Algorithm Bellman-Ford, unlike many other graph algorithms, more convenient to represent the graph
as a single list of all the edges (and not the lists of edges - the edges of each vertex). In here
Starts implement data structure for an edge. The input data for the algorithm are the number
List ribs and the room starting vertex . All the numbers of vertices are numbered by .
struct edge {
int a, b, cost;
};
int n, m, v;
vector <edge>
e;
const int INF = 1000000000;
void solve() {
vector <int> D (n,
INF); d[v] = 0;
for (int i =0; i <n-1; + + I)
for (int j =0; j <m; + + J)
if (d[e[j]. A] <INF)
d[e[j]. B] = Min (d[e[j]. B], D[e[j]. A] +
E
[j]. Cost);
/ / O d, for example, to screen
}
Check "if (d [e [j]. A] <INF)" is needed only if the graph contains edges of negative weight: without such checks
would place the relaxation of the vertices to which the path has not yet been found, and would appear
incorrect distance form , Etc.
An improved
This algorithm can speed up a few: often the answer is already in several phases, and the remaining phases of any useful
work does not happen, only wasted scans all edges. Therefore, we will keep the flag that something has changed in the
current phase or not, and if at some stage, nothing happened, then the algorithm can be stopped. (This optimization does not
improve the asymptotic behavior, ie, on some graphs will continue to need all phase, but significantly accelerates the
behavior of the algorithm "on average", ie on random graphs.)
With this enhancement becomes generally unnecessary to manually limit the number of phases of the
algorithm number - He stops after the desired number of phases.
void solve() {
vector <int> D (n,
INF); d[v] = 0;
for (,;) {
bool any = false;
for (int j =0; j <m; + + J)
if (d[e[j]. A] <INF)
if (d[e[j]. B] > D[e[j]. A] + E[j]. Cost) {
d[e[j]. B] = D[e[j]. A] + E[j].
Cost; any = true;
}
if (! Any) break;
}
/ / O d, for example, to screen
}
Restoration paths
Let us now consider how we can modify the algorithm of Bellman-Ford so that he not only found the length
of the shortest paths, but also allows you to restore themselves shortest paths.
For this array's have another In which each vertex will store its "ancestor", ie
is the penultimate vertex in the shortest path leading to it. In fact, the shortest path to some vertex
is the shortest path to some vertex , Which attributed to the end of the summit .
Note that the algorithm of Bellman-Ford is working on the same logic: it is, assuming that the shortest distance to a vertex already
counted, trying to improve the shortest distance to another vertex. Consequently, at the moment we just need to improve to
We present the implementation of the Bellman-Ford with the restoration of some ways to a given node :
void solve() {
vector <int> D (n,
INF); d[v] = 0;
vector <int> P (n, -1);
for (,;) {
bool any = false;
for (int j =0; j <m; + + J)
if (d[e[j]. A] <INF)
if (d[e[j]. B] > D[e[j]. A] + E[j]. Cost)
{ d[e[j]. B] = D[e[j]. A] + E[j].
Cost;
p[e[j]. B] = E[j].
A; any = true;
}
if (! Any) break;
}
if (d[t] == INF)
cout << "No path from" << V << "To" << T << ".";
else {
vector <int> Path;
for (int cur = t; cur! = -1; cur =
p[cur]) path.push_back
(cur);
reverse (path.begin(), Path.end());
cout << "Path from" << V << "To" << T << ":"; for
(size_t i =0; i <path.size(); + + I)
cout << Path[i] << '';
}
}
Here we first passed by the ancestors, starting from the top, and keep all the traversed path in the list .
from
This list is obtained from the shortest path to, but in reverse order, so we call him
and then draw.
Implementation:
void solve() {
vector <int> D (n,
INF); d[v] = 0;
vector <int> P (n, -
1);int x;
for (int i =0; i <n; + + I) {
x = -1;
for (int j =0; j <m; + + J)
if (d[e[j]. A] <INF)
if (d[e[j]. B] > D[e[j]. A] + E[j]. Cost) {
d[e[j]. B] = Max (-INF, d[e[j]. A] +
E
[j]. Cost);
p[e[j]. B] = E[j].
A; x = e[j]. B;
}
}
if (x == -1)
cout << "No negative cycle from" << V;
else {
int y = x;
for (int i =0; i <n; +
+ I) y = p[y];
vector <int> Path;
for (int cur = y; ; cur =
p[cur]) {
path.push_back (cur);
if (cur == y && path.size() > 1) break;
}
reverse (path.begin(), Path.end());
Because when there is a negative cycle for iterations distance could go far in the minus
(apparently negative numbers to order ), The code has taken additional measures against such
an integer overflow:
In the above implementation is sought negative cycle reachable from some starting vertex ; However, the algorithm can
be modified so that it was looking for just any negative cycle in the graph. To do this, we must put all distances equal to
zero and not infinity - as if we
looking for the shortest path from all vertices at the same time; correctness of the negative cycle
detection is not affected.
Extras on this task - see separate article "Search the negative cycle in the graph".
See also the list of tasks in the article "Search the negative cycle".
Leviticus algorithm finding the shortest paths
from a given vertex to all other vertices
Let a graph with N vertices and M edges, each of which lists its weight Li. Also given starting vertex V0.
Required to find the shortest path from vertex V0 to all other vertices.
Leviticus algorithm solves this problem very efficiently (about the asymptotic behavior and speeds see below).
Description
Let array D [1 .. N] will contain the current shortest path lengths, ie Di - is the current length of the shortest path from vertex
to vertex V0 i. Originally filled array D values "infinity", except DV0 = 0. By
completion of the algorithm, this array will contain the final shortest distance.
Let array P [1 .. N] contains the current ancestors, ie Pi - the pinnacle preceding vertex i in the shortest
path from the vertex V0 to i. As well as an array D, array P is gradually changed in the course of the
algorithm, and it receives the end of the final values.
Now, actually, the algorithm Leviticus. At each step, supported by three sets of vertices:
● M0 - tops, distance to which has already been calculated (but perhaps not completely);
The vertices in the set M1 is stored in the form of a bidirectional queue (deque).
Initially all vertices are placed in the set M2, except vertices V0, which is placed in the set M1.
At each step of the algorithm, we take the top of the set M1 (pull out the top element of the queue). Let V - is the selected
vertex. Translate this vertex in the set M0. Then review all edges emanating from that vertex. Let T - is the second end of
the current edge (ie not equal to V), and L - is the length of the current edge.
● If T belongs to M2, then transferred to a T set M1 in the queue. DT is set equal to DV + L.
● If T belongs to M1, we try to improve the value of DT: DT = min (DT, DV + L). The very top of
T does not move in the queue.
● If T is in M0, and if possible to improve DT (DT> DV + L), then improving DT, and top T return to
the set M1, placing it in the top of the queue.
Of course, whenever you update the array D must be updated and the value in the array P.
Implementation Details
Create an array ID [1 .. N], in which each vertex will be stored, what set it belongs: 0 - if M2 (ie, the distance is equal to infinity), 1 - if
M1 (ie the vertex is in the queue ) 2 and - if M0 (some
path has been found, the distance is less than infinity).
Queue processing can implement standard data structure deque. However, there is a more efficient way. First, obviously, in the queue
at any one time will be a maximum of N elements stored. But, secondly, we can add elements in the beginning and the end of the
queue. Consequently, we can arrange a place on the array size N, but you have to let it loop. Ie do array Q [1 .. N], pointers (int) to the
first element QH and the element after the last QT. The queue is empty when QH == QT. Adding to the end - just a record in Q [QT] QT
and increase by 1; if QT then went beyond the line (QT == N), then do the QT = 0. Adding the queue - reduce QH-1, if it has moved
beyond the stage of (QH == -1), then do the QH = N -1.
Asymptotics
I do not know more or less good asymptotic estimate of this algorithm. I have met only estimate O (NM)
of the similar algorithm.
However, in practice, the algorithm has proven itself very well: while I appreciate his work as O (M
log N), although, I repeat, is exclusively Experimental appraisal.
Implementation
int main ()
{
int n, v1, v2;
graph g (n);
Graph reading ... ...
}
Floyd's Algorithm-Uorshella finding
shortest paths between all pairs of
vertices
Given a directed or undirected weighted graph with vertices. Required to find the values of all
variables - The length of the shortest path from vertex to the top .
It is assumed that the graph contains no cycles of negative weight (then answer between some pairs of
vertices may simply not exist - it will be infinitely small).
This algorithm has been simultaneously published in the papers of Robert Floyd (Robert Floyd) and Stephen Uorshella
(Warshall) (Stephen Warshall) in 1962, after whom this algorithm is called nowadays. However, in 1959, Bernard Roy
(Bernard Roy) published essentially the same algorithm, but its publication
unnoticed.
In this case the not change during the transition from The second to -Th phase.
● "New" was the shortest path better The "old" way.
This means that the "new" shortest path passes through the vertex . Just note that we do not lose
generality, considering only more simple way (ie paths that do not pass on some vertex twice).
And
Then we note that if we break this "new" way top two halves (one going another
- ), Then each of these halves is not comes to the top . But then it turns out that the length of each
these halves was deemed a further second phase or even earlier, and it is sufficient to
simply take the sum , And give it the length of the "new" shortest path.
Combining these two cases, we obtain that The second phase is required to recalculate the length of
the shortest paths between all pairs of vertices and follows:
Thus, all the work that is required to produce the second phase - is to iterate over all pairs of vertices and
recalculate the length of the shortest path between them. As a result, after the The second phase in the matrix
will be recorded length of the shortest path between
distances and Or, if the path between the vertices
does not exist.
The last remark should be done - that can be not to create a separate
Second phase: all the changes can be made
Matrix temporary matrix of shortest paths on immediately
in a matrix. In fact, if we have improved (reduced) a value in the matrix of distances, we could not degrade thereby
length of the shortest path for some other pairs of vertices processed later.
Asymptotics algorithm clear is .
Implementation
Fed to the input of the program graph specified as the adjacency matrix - two-dimensional array size ,
in which each element specifies the length of the edges between the vertices.
It is assumed that if some vertices between two there is no edge, The adjacency matrix, was recorded for a
large number (large enough that it is greater than the length of any path in the graph); then this edge will
always be profitable to take, and the algorithm works correctly.
However, unless you take special measures, in the presence of edges in the graph negative weight,
в the resulting matrix of the form may appear , , Etc., which, of course, still means that the
respective vertices between the no path at all. Therefore, in the presence of
negative edges of the graph algorithm Floyd better write it so that it did not fulfill the transitions of the states
в which already is "no way":
fact, for those pairs of vertices and Between which it is impossible to go into a cycle of negative weight,
algorithm will work correctly.
For those pairs of vertices, the answer to which does not exist (because of the presence of a negative cycle in the path
between them), Floyd's algorithm to find an answer to a number of (possibly highly negative, but
not required). Nevertheless, it is possible to improve the algorithm of Floyd, he carefully processed to such
a pair of vertices, and drew for them, for example, .
This can be done, for example, the following criterion "Is not the existence of the way." So, even for
a given graph worked usual algorithm Floyd. Then between the vertices and there is no shortest
path if and only if there is a vertex , Accessible from and from which is achievable for which holds
.
In addition, when using the Floyd algorithm for graphs with negative cycles should remember that arise in
the process of distance can go into much less exponentially with each phase. Therefore, measures should
be taken against an integer overflow, limiting all distances below some value (eg, ).
More information about this task, see separate article: "Finding a negative cycle in the graph".
Shortcuts fixed length, number of tracks fixed length
The following describes these two objectives, built on the same idea: to reduce the problem to the
construction of the power matrix (with the usual multiplication, and modified).
We assume that the graph is given adjacency matrixIe matrix size Where each
element equals one if between these vertices is an edge, and zero if there is no edge. Described
following algorithm works in the case of multiple edges if between some vertices and is at once
. Algorithm also takes into account the correct loops in
ribs, the adjacency matrix should record this number the graph,
if any.
Obviously, in this form, adjacency matrix graph is answer to the problem at -
it contains a number of paths of length between each pair of vertices.
The decision will build iterativelyLet the answer for some is found, we show how to build it
. We denote matrix found answers to And through - Matrix of responses that
to build. Then clear the following formula:
Easy to see that recorded above formula - nothing more than the product of two matrices and in the
the usual sense:
It remains to note that the construction of the power matrix can be done efficiently
using the algorithm Binary exponentiation.
Thus, the obtained solution has the asymptotic and is binary exponentiation Th
degree of adjacency matrix.
Look closely at this formula, it is easy to draw an analogy with the matrix multiplication: in fact, the matrix
is multiplied by the matrix Only in the multiplication operation instead of the sum of all minimum is taken over all :
It remains to note that the construction of the power of this multiplication can be performed effectively by using the
algorithm Binary exponentiationAs the only required for
his property - associativity of multiplication - obviously there.
Thus, the obtained solution has the asymptotic behavior and is binary exponentiation -Th
power of the adjacency matrix of the graph with the modified matrix multiplication.
Minimum spanning tree. Prim's algorithm
Given a weighted undirected graph with and peaks ribs. Required to find a subtree of this graph, which
would connect all the vertices, and thus has the lowest possible weight (ie, the sum of the weights of the
edges). Subtree - a set of edges connecting vertices, and every vertex can reach any other by exactly one
simple way.
This subtree is called a minimal spanning tree, or simply the minimum spanning tree. Easy to
understand that any framework will necessarily contain edge.
Innatural setting This problem is as follows: there cities, and for each pair of known connection
cost them dear (or know that they can not connect). You want to connect all cities so that you can
get from any city to another, and thus the cost of construction of roads would be minimal.
Prim's algorithm
This algorithm is named after the American mathematician Robert Primus (Robert Prim), which opened this algorithm in 1957,
however, in 1930, this algorithm has been opened by the Czech mathematician Wojtek Jarnik (Vojtěch Jarník). Furthermore, Edgar
Dijkstra (Edsger Dijkstra) in 1959 as invented this algorithm independently.
Proof
Suppose that the graph was connected, ie answer exists. We denote the skeleton found Prim algorithm, and through
- Minimum core. Obviously, that really is the skeleton (ie, the subtree of the graph). Show
that weight and coincide.
Consider the first time when it is added edge that is not in the best frame
. We denote this edge through Ends it - through and a plurality of incoming at that time in the skeleton vertices -
by (according to the algorithm , Or vice versa). Optimally skeleton and peaks
connected in some way ; in this way we find any edge, one end of which lies in, and the other -
instead of the edge, then this means that the
not. Since Prim algorithm chosen edge weight of the rib greater than or equal to
weight of the edge.
. By just what to say, as a result of the weight of the core could
Now remove from the edge, and add an edge not
increase (decrease it too could not because was optimal). Furthermore, not ceased to be a skeleton
(The fact that the connection is not broken, it is easy to make: we closed the way to the ring, and then removed from
this
cycle one edge).
Thus we have shown that we can choose the optimum frame thus it will comprise a rib. Repeating
this procedure as many times, we find that we can choose the optimum frame so that it matches
s. Consequently, the weight of the constructed algorithm
Prima minimal, as required.
Implementation
Time of the algorithm depends essentially on how we search the minimum of the next edge of suitable
edges. There may be different approaches lead to different asymptotic behavior and different
implementations.
Trivial Pursuit: algorithms for and
If we look for an edge every time just browsing among all possible options, then asymptotically
will be required to show Ribs to find among all admissible edge with the least weight.
Album asymptotic behavior of the algorithm in this case will be That in the worst case, there is -
too slow algorithm.
This algorithm can be improved if the view each time not all the edges, but only on one edge of each vertex is selected.
For this example, you can sort the edges of each vertex in the order of increasing weights, and store a pointer to the first
valid edge (recall allowed only those edges that are not yet in the set of selected vertices). Then, if recalculate these
pointers for each
add edge to the backbone, the total asymptotic behavior of the algorithm
will But pre-
need to sort all the edges in That in the worst case (for a dense graphs)
gives an asymptotic .
Below we consider two slightly different algorithm: for dense and sparse graphs, received as a
result significantly better asymptotic behavior.
Implementation of Prim's algorithm for the graph given by the adjacency matrix :
// input int n;
vector <vector <int>> G;
const int INF = 1000000000; / / value "Infinity"
// algorithm
vector <bool> Used (n);
vector <int> Min_e (n, INF), Sel_e (n, -
1); min_e[0] = 0;
for (int i =0; i <n; + +
I) { int v = -1;
for (int j =0; j <n; + + J)
if (! Used[j] && (v == -1 | | Min_e[j]
<Min_e[v])) v = j;
if (min_e[v] == INF) {
cout << "No MST!";
exit(0);
}
used[v] = true;
if (sel_e[v] , = -1)
cout << V << "" << Sel_e[v] << Endl;
Marks the And then looks at all the edges of this vertex, recounting their labels.
Within the meaning of the algorithm is exactly the same, but now we can find the minimum edge during .
On the other hand, the time for
recalculation pointers now be That is worse than in
the above algorithm.
If we consider that there will be pointers and conversions search for the minimum edge, then
asymptotic behavior of the total
amount - For sparse graphs is better than both
above algorithm, but on dense graphs, this algorithm will be slower previous.
Implementation of Prim's algorithm for graph adjacency lists specified :
// input int n;
vector <vector <pair <int,int>>> G;
const int INF = 1000000000; / / value "Infinity"
// algorithm
vector <int> Min_e (n, INF), Sel_e (n, -
1); min_e[0] = 0;
set <pair <int,int>> Q;
q.insert (make_pair
(0,0));for (int i =0; i <n;
+ + I) {
if (q.empty()) {
cout << "No MST!";
exit(0);
}
int v = q.begin()->
Second; q.erase
(q.begin());
if (sel_e[v] , = -1)
cout << V << "" << Sel_e[v] << Endl;
The input is the number of vertices and the adjacency lists: - A list of all edges emanating from the vertex to
as pairs (the second end of the edge weight of the edge). The algorithm supports two arrays
value stores
the smallest possible weight from the top edge, and the element contains the lowest end of the rib
(It is necessary to bring the edges of the reply). Additionally, a queue all of the nodes in increasing order of their tags. The
algorithm makes steps, each of which selects the top with the smallest label
(Just removing it from the queue), and then looks at all the edges of this vertex, recounting their labels (when
calculated from the queue, we remove the old value, and then put back a new one).
for all vertices not yet selected). Moreover, the Dijkstra's algorithm has also
two embodiments: in and (Of course we do not consider here the possibility
the use of complex data structures to achieve even smaller asymptotics).
If you look at Prima and Dijkstra algorithms more formally, it turns out that they are generally identical to each other
except the weighting function vertices: if Dijkstra each vertex supported length of the shortest path (ie the sum of the
weights of some edges), the algorithm Prima each vertex attributed only to the minimum weight edge leading into the set
of vertices already taken.
At the implementation level, this means that after the addition of the next vertex in the set of selected vertices
when we begin to view all the edges from this vertex, the algorithm Prima pointer
updated weight of the edge , And Dijkstra - mark distance updated sum tags
and edge . Otherwise, these two algorithms can be considered identical (although they decide to
weight completely
different tasks).
● Minimum frame is the skeleton of a minimum weight the heaviest edge. Most clearly this
statement is clear if we consider the work Kruskal's algorithm.
● Minimality criterion skeleton: backbone is minimal if and only if if for any edges not belonging to the
skeleton, the loop formed by this edge by adding to the core, no edges heavier than the edge. In fact, if for
some edge turned out that it is easier for some ribs formed loop, the frame can be obtained with a smaller
weight (an edge is added to the skeleton, and by removing the heaviest edge of the loop). If this condition is
not fulfilled for any edge, then all these edges do not improve the weight of the core when they are added.
Minimum spanning tree. Kruskal's algorithm
Given a weighted undirected graph. Required to find a subtree of this graph, which would connect all the vertices, and thus
has the least weight (ie, the sum of the weights of the edges) of all. This subtree is called a minimal spanning tree, or simply
the minimum core.
It will discuss some important facts related to the minimum spanning tree, then be
considered Kruskal's algorithm in its simplest implementation.
Kruskal's algorithm
This algorithm has been described by Kruskal (Kruskal) in 1956
Kruskal's algorithm initially assigns each vertex in your tree, and then gradually brings these trees, combining the
two at each iteration some wood some edge. Before starting the algorithm, all the edges are sorted by weight (in
decreasing order). Then begins the process of
Unions: get over all the edges from first to last (in the sort order), and if the current edge of its ends belong to different subtrees,
these subtrees are combined, and the edge is added to the answer. At the end of sorting all edges vertices will belong to the
same subtree, and the answer is found.
int cost = 0;
vector <pair <int,int>> res;
Description
Just as in the simple version of Kruskal's algorithm, we can sort all the edges in non-decreasing weight. Then put each node
in your tree (ie its set) by calling the DSU MakeSet - it will take in the amount of O (N). Loop through all the edges (in the sort
order), and for each edge in O (1) define, whether it ends belong to different trees (using two calls FindSet O (1)). Finally, the
union of two trees
will be calling the Union - also O (1). Overall, we obtain the asymptotic behavior of O (M log N + N + M) = O (M log N).
Implementation
To reduce the amount of code and carry out all operations are not as separate functions, and directly in the
code of Kruskal's algorithm. Here we will use a randomized version of the DSU.
int m;
vector <pair <int, pair <int,int> >> g; / / Weight - the top one - the top 2
Graph reading ... ...
int cost = 0;
vector <pair <int,int>> res;
where the matrix T is obtained from the matrix A inverse Conductor resistance (Aij - opposite number to
Resistance conductor between points i and j) transformation matrix described in the Kirchhoff's theorem,
and designation of T (i) denotes the deletion of the row and column number i, and T (i, j) - the deletion of two
rows and columns i and j. Kirchhoff theorem gives this formula geometric meaning.
Prüfer code. Cayley formula.
The number of ways to make a graph connected
In this article we consider the so-called Prüfer codeWhich represents a coding method for uniquely tagged
using a sequence tree properties.
Using Prüfer codes demonstrates proof Cayley's formula (Specifying the number of spanning
trees in a complete graph), as well as solving the problem of the number of ways to add to the
given graph edges to turn it into a coherent.
Note. We will not consider trees consisting of a single vertex, - is a special case, where many of the
statements degenerate.
Prüfer code
Prüfer code - a way bijective coding labeled trees with vertices by a sequence integers in the interval.
In other words, the code Prüfer - is
bijection among all spanning trees of a complete graph and numerical sequences.
Although use Prüfer code for storing and manipulating trees impractical due to the specificity of representation
Author - Heinz Prüfer (Heinz Prüfer) - suggested this code in 1918 as proof of Cayley's formula (see below).
- Node number associated with the smallest at the time sheet - ie is a number in the interval .
Algorithm for computing Prüfer code is easy to implement with the asymptotic Simply maintaining
structure for extracting minimum data (e.g., or in C +
+) Containing a list of all current leaves:
[i]; result[iter] = V;
if (- Degree[v] == 1)
leaves.insert (v);
}
return result;
}
However, the construction of Prüfer code can be implemented in linear time and that is described in the next section.
vector <int>
Result; int leaf =
ptr;
for (int iter =0; iter <n-2; + +
Iter) { int next =
parent[leaf];
result.push_back (next); -
Degree[next];
if (degree[next] == 1 && Next <ptr)
leaf = next;
else {
+ + Ptr;
while (ptr <n && degree[ptr] ! =
1) + + Ptr;
leaf = ptr;
}
}
return result;
}
Comment on this code. The main function here - Which returns a code for Priifer
tree set in global variables (Number of vertices) and (Adjacency lists specifying the graph). Initially
we find for each vertex of its ancestor - Ie ancestor of which this node will have
the time of disposal of wood (all we can find in advance, using the fact that the maximum peak
never removed from the tree). Also, we find for each vertex its degree . Variable -
a moving pointer ("candidate" for a minimum sheet) which changes only in the direction always
increase. Variable - This is the current sheet with a minimum number. Thus, each iteration
in response, as well as making sure there
Prüfer code is to add was not
less than the current
candidate If it turned out less, we simply assign And
otherwise - move the pointer before the next sheet.
As can be seen easily by the code, the asymptotic behavior of the algorithm is really a pointer undergo only
changes, and all other parts of the algorithm is obviously work in linear time.
This is easily understood if we note that the vertex is removed from the tree at a time when its degree
is equal to one - ie at this point all edges adjacent to it, but one, has been removed. (For the two
remaining vertices after building code this statement is also true.)
Thus, we find the first edge, remote Priifer code. Add this edge back, then reduce power
both ends of the rib.
We will repeat this operation until you have reviewed all the code Priifer: search with minimal vertex
In the end we are left with only two vertices - Those peaks, which algorithm
Prüfer left unremoved. Connect them an edge.
The algorithm is complete, the desired tree is built.
Realize This algorithm is easily during : Maintaining a data structure for
in C + +) numbers of all
extracting the minimum (e.g., or vertices
with And removing from it every time at least.
Here are the appropriate implementation (where the function returns a list of the edges of the
required tree):
Cayley formula
Cayley's formula states that number of spanning trees in full labeled graph of vertices is equal to:
There are many evidence this formula, but the proof using codes Priifer clearly and
constructively.
In fact, any set of numbers from the interval corresponds uniquely to a tree of vertices. Unique codes Priifer
Let - The degree of the vertices in the backbone. Sum of the vertex degrees is equal to twice the number of edges, so:
If I-vertex has degree, then it enters the code Priifer times. Prufer Code for the tree of
vertices has a length . Number of ways to choose a set of numbers, where the number occurs exactly
times power multinomial coefficient (By analogy with binomial coefficient)
Given the fact that each edge adjacent to the Th vertex multiplies response We find that the answer
provided that the degrees of vertices are Is equal to:
To answer this problem we must sum over all possible valid formula sets :
Comparing this with the previous formula, we find that if we introduce the notation :
(This formula holds for Although formal proof of it should not have.)
One of the common "life" performances of this problem - the following: known Currency RatesIe
courses transfer from one currency to another. You want to know whether a certain sequence of
exchanges to benefit, ie started with one unit of a currency, receive as a result of more than one
unit of the same currency.
struct edge {
int a, b, cost;
};
int n, m;
vector <edge>
e;
const int INF = 1000000000;
if (x == -1)
cout << "No negative cycle found.";
else {
int y = x;
for (int i =0; i <n; +
+ I) y = p[y];
vector <int> Path;
for (int cur = y; ; cur =
p[cur]) {
path.push_back (cur);
if (cur == y && path.size() > 1) break;
}
reverse (path.begin(), Path.end());
cout << "Negative cycle:";
for (size_t i =0; i <path.size(); + + I)
cout << Path[i] << '';
}
}
Between which there exists a shortest path (i.e., it has an infinitesimal amount).
Again, more detailed explanations contained in description of the algorithm Floyd-
UorshellaAnd here we present only the results.
After Floyd's algorithm-Uorshella work for the input graph, iterate over all pairs of vertices And
for each such pair, check infinitesimal shortest path from to or not. To do this, let's look over the third
top, and if it has turned (Ie, it lies in a cycle of negative weight), and she is achievable
and out of reach - the path could have infinitesimal length.
Implementation:
Algorithm
Please check whether there is an Euler path. Then we find all simple cycles and combine them into one -
it will be an Euler tour. If the graph is that the Euler path is not a cycle, then add the missing edge, find an
Euler tour, then remove the extra edge.
To test whether there is an Euler path, you need to use the following theorem. Euler cycle exists if and only if the degrees of all
vertices are even. Eulerian path exists if and only if the number of vertices equals the odd powers of two (or zero if there is Euler
cycle).
Also, of course, the graph must be sufficiently connected (ie, if we remove from it all isolated vertices,
then should get a connected graph).
Search all the loops and will combine them a recursive procedure:
The complexity of this algorithm is obviously linear in the number of edges. But the
stack St;
in St put any vertex (top of page); while St
is not empty
Let V - value on top of St; if
the power (V) = 0, then
add V to the answer;
remove V from the top
of St;
otherwise
find any edge going from V; remove
it from the graph;
the second end of the rib put in St;
It is easy to verify the equivalence of these two forms of the algorithm. However, the second
shape is obviously faster running, the code will not be greater.
Implementation
The following program searches for and displays Euler cycle or path in the graph, or displays -1 if it does not exist.
First, the program checks the degree of vertices if the vertices with odd degree is not, then the graph has an Euler tour if
there are two vertices with odd degree, then the graph has an Euler path only (no Euler cycle), if such heights greater than
2, then graph no Euler cycle or Euler path. To find an Euler path (not
cycle), proceed as follows: if V1 and V2 - that two vertices of odd degree, then just add an edge (V1, V2), in the resulting
graph will find an Euler tour (he obviously will exist), and then remove from the answer "fictitious "rib (V1, V2). Euler tour
will look exactly as described above (non-recursive version), and at the same time at the end of this algorithm check was
connected graph or not (if the graph is not connected, then
termination of the algorithm, some remain in the column ribs, and in this case we must derive -1).
Finally, the program takes into account that in the graph can be isolated vertices.
int main () {
int n;
vector <vector <int>> g (n, vector <int> (n));
Reading ... graph adjacency matrix ...
if (v1! = -1)
+ + G [v1] [v2], + + g [v2] [v1];
if (v1! = -1)
for (size_t i = 0; i +1 <res.size (); + + i)
if (res [i] == v1 && res [i +1] == v2 | | res [i] == v2
&& Res [i +1] == v1)
{
vector <int> res2;
for (size_t j = i +1; j <res.size ();
+ + j) res2.push_back (res
[j]);
for (size_t j = 1; j <= i; + +
j) res2.push_back (res
[j]);
res = res2;
break;
}
if (bad)
puts ("-1");
else
for (size_t i = 0; i <res.size ();
+ + i) printf ("% d", res
[i] +1);
}
Checking on the acyclic graph and
finding cycle
Let a directed or undirected graph without loops and multiple edges. Want to check whether it is acyclic,
and if not, then find any cycle.
Solve this problem by using DFS in O (M).
Algorithm
We carry out a series of searches in depth in the graph. Ie from each vertex, which we had never come, run dfs that at
the entrance to the top will paint it gray, and on leaving - in black. And if dfs tries to go into a gray top, it means that we
have found a cycle (if
undirected graph, the cases where the depth-first search of some vertex tries to go to the parent are not
Implementation
Here is an implementation for the case of a directed graph.
int n;
vector <vector <int>> g;
vector <char> cl; vector
<int> p;
int cycle_st, cycle_end;
int main () {
Graph reading ... ...
if (cycle_st == -1)
puts ("Acyclic");
else {
puts ("Cyclic"); vector
<int> cycle;
cycle.push_back (cycle_st);
for (int v = cycle_end; v! = cycle_st; v =
p [v]) cycle.push_back (v);
cycle.push_back (cycle_st);
reverse (cycle.begin (), cycle.end
()); for (size_t i = 0; i <cycle.size
(); + + i)
printf ("% d", cycle [i] +1);
}
}
Lowest common ancestor. For finding O (sqrt (N))
and O (log N) preprocessing with O (N)
Suppose we are given a tree G. The input receives requests form (V1, V2), for each request is required to find their
least common ancestor, ie vertex V, which lies on the path from the root to the V1, the path from the root to the V2,
and from all such vertices should choose the lowest. In other words, the required vertex V - ancestor and V1, and
V2, and among all such common ancestors selected lower. It is obvious that the lowest common ancestor of
vertices V1 and V2 - it is their common ancestor, which lies on the shortest path from V1 to V2. In particular, for
example, if V1 is the ancestor of V2, then V1 is their least common ancestor.
In English, this problem is called LCA - Least Common Ancestor.
If you use sqrt-decompositionIt is possible to obtain a solution that responds to the request for O (sqrt
(N)) and performs preprocessing for the O (N).
If you use segment treeIt is possible to obtain a solution that responds to the request for O (log (N))
and performs preprocessing for the O (N).
Implementation
Here will be given ready LCA implementation using wood segments:
int main ()
{
graph g;
int root;
Graph reading ... ...
for (; ;)
{
int v1, v2; / / Received a request
int v = lca (v1, v2); / / Response to a request
}
}
Lowest common ancestor. Being in O (log N)
(binary lifting method)
Suppose we are given a tree G. The input receives requests form (V1, V2), for each request is required to find their
least common ancestor, ie vertex V, which lies on the path from the root to the V1, the path from the root to the V2,
and from all such vertices should choose the lowest. In other words, the required vertex V - ancestor and V1, and
V2, and among all such common ancestors selected lower. It is obvious that the lowest common ancestor of
vertices V1 and V2 - it is their common ancestor, which lies on the shortest path from V1 to V2. In particular, for
example, if V1 is the ancestor of V2, then V1 is their least common ancestor.
In English, this problem is called LCA - Least Common Ancestor.
This algorithm will be discussed, which is written much faster than the one described here.
Asymptotics of this algorithm will be equal for preprocessing O (N log N) and the response to each
request for O (Log N).
Algorithm
Predposchitaem for each vertex of its ancestor 1st, 2nd ancestor, 4th, etc. Denote the array through P, ie P [i] [j] -
it 2j-th ancestor vertex i, i = 1 .. N, j = 0 .. • logN •. Also, for each vertex find sunset times in and output depth-first search (see
"Depth-First Search") - That we need to determine in O (1) whether one vertex
ancestor of the other (not necessarily direct). This preprocessing can be done in O (N log N).
Suppose now entered another request - a pair of vertices (A, B). Immediately check whether one vertex is an ancestor of the
other - in this case, it is the result. If A is not an ancestor of B, and B is not an ancestor of A, then we climb the ancestors A, until
we find the highest (ie, closest to the root) of the vertex, which is still
is not an ancestor (not necessarily direct) B (ie, a vertex X, that X is not an ancestor of B, and P [X] [0] - the
ancestor of B). At the same time finding the vertex X is in O (log N), using an array of P.
We describe this process in more detail. Let L = • logN •. Suppose first that I = L. If P [A] [I] is not an ancestor of B, then assign A = P [A]
[I], and reduce I. If P [A] [I] is an ancestor of B, then just reduce I. Obviously, when I would be less than zero, the vertex A just and will
be required vertex - ie such that A is not an ancestor of B, but P [A] [0]
- Ancestor of B.
Now, obviously, the answer to the LCA will be P [A] [0] - ie smallest vertex among the ancestors of the
original vertices A, which is also the ancestor of B.
Asymptotics. The whole algorithm is responding to a request from the change of I L = • logN • to 0, and
check each step in O (1) whether one vertex is an ancestor of the other. Consequently, for each query
answer is found in O (log N).
Implementation
int n, l;
vector <vector <int>> g;
vector <int> tin, tout;
int timer;
vector <vector <int>> up;
int main () {
}
Lowest common ancestor. Finding the O (1) with
preprocessing O (N) (algorithm Farah-Colton and
Bender)
Suppose we are given a tree G. The input receives requests form (V1, V2), for each request is required to find their
least common ancestor, ie vertex V, which lies on the path from the root to the V1, the path from the root to the V2,
and from all such vertices should choose the lowest. In other words, the required vertex V - ancestor and V1, and
V2, and among all such common ancestors selected lower. It is obvious that the lowest common ancestor of
vertices V1 and V2 - it is their common ancestor, which lies on the shortest path from V1 to V2. In particular, for
example, if V1 is the ancestor of V2, then V1 is their least common ancestor.
In English, this problem is called LCA - Least Common Ancestor.
Farah algorithm described here-Colton and Bender (Farach-Colton, Bender) is asymptotically optimal, and thus a relatively
simple (compared to other algorithms, for example, gate-Vishkina).
Algorithm
Use the classical reducing the problem of LCA to the problem RMQ (Minimum interval) (for more details see Lowest
common ancestor. Finding for O (sqrt (N)) and O (log N) with preprocessing O (N)). Now learn to
RMQ solve the problem in this particular case with preprocessing O (N) and O (1) on request.
Note that the problem RMQ, to which we have reduced the problem LCA, is very specific: any two adjacent
elements in the array differ by exactly one (As array elements - it is nothing like the height of vertices visited in
the traversal, and we either go to the descendant, then the next item will be 1 more or go to the ancestor, then the
next item will be 1 less). Actually algorithm Farah-Colton and Bender just is a solution of this problem RMQ.
Let A array over which queries are running RMQ, and N - size of the array.
We first construct an algorithm that solves this problem with preprocessing O (N log N) and O (1) on inquiry. It's
easy: create a so-called Sparse Table T [l, i], where each element T [l, i] equal to the minimum A
on the interval [l; l +2 i). Obviously, 0 <= i <= • log N •, and therefore the size of Sparse Table is O (N log N). Build it
also easy for the O (N log N), if we note that T [l, i] = min (T [l, i-1], T [l +2 i-1, i-1]). How now to respond to every request RMQ O (1)?
Let received a request (l, r), then the answer would be min (T [l, sz], T [r-2sz +1, sz]), where sz - the largest power of two
not exceeding r-l +1. Indeed, we seem to take the interval (l, r) and cover it with two runs of length 2sz - one
starting at l, and the other ending in r (and these segments overlap, which in this case does not bother us). To
really achieve the asymptotics O (1) on the request, we must predposchitat sz values for all possible lengths from
1 to N.
We will now describe to improve this algorithm to the asymptotic behavior of O (N).
We divide the array A into blocks of size K = 0.5 log2 N. For each block, calculate the minimum element in it and
his position (as for the solution of LCA are important to us not the lows, and their positions). Let B - is an array
of size N / K, composed of these minima in each block. Construct the array B Sparse Table, as described
above, the size of Sparse Table and time of its construction will be:
Now we just need to learn how to quickly respond to requests RMQ within each block. In fact, if the request comes RMQ
(l, r), then if r and l are located in different blocks, the answer will be a minimum of the following values: at least a block l,
starting from l to the end of the block, then the minimum units after l to r and
(Not included), and finally the minimum block r, from the beginning of the block to r. At the prompt "at least in the"
we can be responsible for O (1) using the Sparse Table, there were only questions RMQ in blocks.
Here we use the "+ -1 property." Note that, if within each block of each of its elements take the first element,
then all blocks will be uniquely determined by the sequence of length K-1, consisting of the numbers + -1.
Consequently, the amount of the various blocks will be:
Thus, the number of different blocks will be O (sqrt (N)), and therefore we can predposchitat results inside RMQ
all the different blocks in O (sqrt (N) K2) = O (sqrt (N) log2 N) = O (N). From an implementation standpoint, we can characterize
each block bitmask length K-1 (which obviously fits into standard type int), and
store predposchitannye RMQ from an array R [mask, l, r] size O (sqrt (N) log2 N).
So, we have learned predposchityvat RMQ results within each block, as well as by the RMQ over blocks all
for a total of O (N), and to respond to every request RMQ O (1) - using only the precomputed values in the worst
case four: in block l, in block r, and on the blocks between l and r is not inclusive.
Implementation
At the beginning of the program are constants MAXN, LOG_MAXLIST and SQRT_MAXLIST, determine
the maximum number of vertices in the graph, which, if necessary, should be increased.
/ / Walk graph
void dfs (int v, int curh)
{h [v] = curh;
a_pos [v] = (int) a.size
(); a.push_back (v);
for (size_t i = 0; i <g [v]. size
(); + + i) if (h [g [v] [i]]
== -1) {
dfs (g [v] [i], curh
+1); a.push_back (v);
}
}
// O (N) preprocessing
void build_lca () {
int sz = (int) a.size ();
block = (log (sz) + 1) /
2;
int blocks = sz / block + (sz% block? 1: 0);
/ / Precalc logarithms
for (int i = 0, j = 0; i <sz; +
+ i) {if (1 << (j +1) <=
i) + + j; log2 [i] = j;
}
}
Algorithm
Construct the array A Cartesian tree where each node is a key position i, and priority - the sheer number of A [i] (it is assumed
that in the Cartesian tree priorities are ordered from smaller to larger radically). Such
tree can be constructed in O (N). Then inquiry RMQ (l, r) is equivalent to the request LCA (l ', r'), where l '- the vertex
corresponding to the element A [l], r' - corresponding to the A [r]. Indeed, LCA find a vertex which is
the key lies between l ', r', i.e. by position in the array A will be between l and r, and wherein the vertex
closest to the top, i.e. with the lowest priority, ie the lowest value.
LCA problem we can solve in O (1) preprocessing with O (N) using Farah algorithm-Colton and BenderWhich,
interestingly, reduces the problem back to the LCA problem RMQ, but a special form.
Lowest common ancestor. Finding for offline
(Tarjan algorithm)
queries of the
Given tree with vertices and given form . For each request required
find the lowest common ancestor of vertices and that a vertex, which is farthest from the root,
and this is the ancestor of both peaks and.
We consider the problem in the offline mode, ie Considering that all requests are known in advance. Described
following algorithm allows to answer all requests for the total time Ie at sufficiently
fo
large r on request.
Tarjan's algorithm
The basis for the algorithm is a data structure "The system of disjoint sets"Which was invented
Tarjanne (Tarjan).
Algorithm is actually a detour into the depths of the root of the tree, in the which are gradually responding to requests. Namely,
the answer to the query is when traversal depth is at the top
And the apex already has had, or vice versa.
So let traversal depth is at the top (And have already been implemented in the transitions of her sons), and found that
for some query vertex already has had a circuit in depth. Then learn how to find
these two vertices.
Note that is either the very top, or one of its ancestors. So, we need to
find the lowest vertex of ancestors (including herself), for which the peak is a descendant.
Note that for fixed on such grounds (ie what the lowest ancestor and what is the ancestor-
the vertices) tree top tree split into a set of disjoint classes. For each
ancestor vertices her class contains the vertex itself and all subtrees with roots in those of its
sons, who are "left" of the way up (I.e., that have been processed earlier than was achieved ).
We need to learn how to effectively support all of these classes, for which we apply the data structure "system of disjoint sets." Each
class will meet in the structure set, and
representative for this set, we define the quantity - that top , Which defines this class.
We consider in detail the implementation of traversal depth. Suppose we are at some vertex . Place it in the
separate class in the structure of disjoint sets, . As usual in the bypass
depth, we sort all outgoing edges . For each such we first need to call the traversal
depth of this peak, and then add the top with all its subtree in the class top. It
operation is realized data structures, "a system of disjoint sets", followed by
installation for a representative set (after the merger because the class representative
could change). Finally, after processing all the edges, we iterate through all the queries of the And if there
form was
marked as visiting a circuit in depth, the response to this request will be
top . It is easy to see that for each request
this condition (that one vertex is the current request, and the other has had before) is executed exactly once.
We estimate the asymptotics. It consists of several parts. First, it is the asymptotic behavior of traversal depth, which in
this case is. Secondly, this business combination sets that
amounting to expend all reasonable operations. Thirdly, it is for each request verification conditions
(Twice on request) and the definition of the result (once at your request), each, again, for all reasonable
performed for . Total asymptotics obtained That is sufficiently large
( ) Response for one request.
Implementation
We present a complete implementation of this algorithm, including a slightly modified (with support
int main() {
Graph reading ... ...
// read requests
for (,;) {
int a, b = ...; / / another request
- A, - b; q[a].
Push_back (b);
q[b]. Push_back
(a);
}
The task is to find the maximum flow. It will discuss the method of Edmonds-Karp, who works for the O (N M2), or (another estimate) O
(FM), where F - the value of the desired flow. Algorithm was proposed in 1972.
Algorithm
Residual bandwidth called bandwidth edges minus current flow along this edge. It must be remembered that if a
stream flows through the oriented edge, then the so-called inverse edge (directed in the opposite direction), which
will have zero capacity, and which will take place the same value on the stream, but with a minus sign. If the rib was
unoriented, as it would be oriented into two edges with the same bandwidth, and each of these edges is the inverse
of the other (if one proceeds flow F, then proceeds differently-F).
General schematic Edmonds-Karp algorithm is as follows. Please believe the flow is zero. Then look for
complementary way, ie simple path from S to T on those edges whose residual bandwidth
strictly positive. If supplementing the way had been found, then zooms the current flow along this path. If the
same path is not found, then the current flow is highest. For supplementing the search path can be used as
Crawling in a widthAnd Crawling in depth.
Consider a more accurate procedure to increase the flow. Suppose we found some complementary way, then let C - the smallest of
the residual capacities of the edges of this path. The procedure is to increase the flow
as follows: for each edge (u, v) perform complementary ways: Fu, v + = C, and Fv, u = - Fu, v (or, equivalently, Fv, u
- = C).
The flux is the sum of all non-negative values of FS, v, where v - any vertex connected to the source.
Implementation
int main ()
{
int n;
cin >> n;
vvint c (n, vint (n));
for (int i = 0; i <n; i
+ +)
for (int j = 0; j <n;
j++) cin>> c
[i] [j];
/ / Source - vertex 0, Stock - top n-1
if (from [n-1] == -
1) break;
int cf = inf;
for (int cur = n-1; cur! = 0;)
{
int prev = from [cur];
cf = min (cf, c [prev] [cur]-f [prev]
[cur]); cur = prev;
}
int flow = 0;
for (int i = 0; i <n; i
+ +) if (c [0]
[i])
flow + = f [0] [i];
}
Maximum flow by pushing predpotoka
in O (N4)
Let a graph G, which revealed two peaks: the source S and drain T, and each edge is defined
bandwidth Cu, v. The flow F may be represented as a stream of a substance which could pass through the network from source to drain
when viewed as a network pipe graph with certain bandwidth. Ie Feed - function Fu,
The task is to find the maximum flow. It will discuss the method of pushing predpotoka working at O (N4), or, more precisely,
for the O (N2 M). The algorithm was proposed by Goldberg in 1985.
Algorithm
The general scheme of the algorithm is as follows. At each step, we will consider some predpotok - ie function that resembles
the properties of the flow, but not necessarily satisfy the law of conservation of flux. At each step, we try to apply any of the
two operations: pushing or lifting the top of the flow. If at some stage it will be impossible to use any of the two operations, we
have found the required flow.
Each vertex is defined by its height Hu, and HS = N, HT = 0, and for any remaining edge (u, v) have Hu
<= Hv + 1.
For each vertex (except S) can determine its excess: Eu = FV, u. Top with positive excess is
called crowded.
Operation push Push (u, v) is applicable if the vertex u is full, the residual bandwidth Cfu, v> 0 and Hu = Hv + 1. Operation push
is to maximize the flow from u to v,
Eu and limited excess residual bandwidth Cfu, v.
Operation lift Lift (u) raises crowded vertex u to the maximum allowable height. Ie Hu = 1 + min {Hv},
where (u, v) - the residual rib.
It remains only to consider the initialization stream. It is necessary to initialize only the following values:
FS, v = CS, v, Fu, S = - Cu, S, the remaining values equal to zero.
Implementation
void push (int u, int v, vvint & f, vint & e, const vvint & c)
{
int d = min (e [u], c [u] [v] - f [u]
[v]); f [u] [v] + = d;
f [v] [u] = - f [u]
[v]; e [u] - = d;
e [v] + = d;
}
void lift (int u, vint & h, const vvint & f, const vvint & c)
{
int d = inf;
for (int i = 0; i <(int) f.size (); i +
+) if (c [u] [i]-f [u] [i]> 0)
d = min (d, h [i]);
if (d == inf)
return;
h [u] = d + 1;
}
int main ()
{
int n;
cin >> n;
vvint c (n, vint (n));
for (int i = 0; i <n; i
+ +)
for (int j = 0; j <n;
j++) cin>> c
[i] [j];
/ / Source - vertex 0, Stock - top n-1
vint h (n);
h [0] = n;
vint e (n);
for (int i = 1; i <n; i
+ +) e [i] = f
[0] [i];
for (;;)
{
int i;
for (i = 1; i <n-1;
i++) if (e[i]> 0)
break;
if (i == n-1)
break;
int j;
for (j = 0; j <n; j + +)
if (c [i] [j]-f [i] [j]> 0 && h [i] == h
[j] +1) break;
if (j <n)
push (i, j, f, e, c);
else
lift (i, h, f, c);
}
int flow = 0;
for (int i = 0; i <n; i
+ +) if (c [0]
[i])
flow + = f [0]
}
Modification of the method for finding
the push predpotoka
maximum flow in O (N3)
It is assumed that you have already read Method push predpotoka for finding the maximum flow O (N4).
Description
Modification is very simple: at each iteration among all crowded vertices we select only those vertices that have
Greatest heightAnd apply a pushing / lifting only to those heights. Moreover, to select nodes with a maximum
height we do not need any data structures, just keep a list of nodes with a maximum height and just recalculate it,
if all the vertices of this list have been processed (then the list will be added to the top of already lower height),
and when new tops crowded with greater height than the list, clear the list and add the top of the list.
Despite its simplicity, this modification reduces the asymptotic behavior of the whole order. To be
precise asymptotic behavior of the algorithm is received O (N M + N2 sqrt (M))That is, in the worst case
O (N3).
This modification was proposed Cheriyanom (Cheriyan) and Maheshwari (Maheshvari) in 1989
Implementation
Here is a ready-made implementation of this algorithm.
Unlike conventional algorithm push - only available array maxh, which will be stored crowded rooms with a maximum
height of vertices. The array size is specified in the variable sz. If at some iteration is that the array is empty (sz == 0), then
we fill it (just passing through all the vertices); if after this array is still empty, the vertices not crowded, and the algorithm
stops. Otherwise, we go over the tops in the list applying to them pushing or lifting. After the operation, push the current
node may cease to be crowded, in this case, remove it from the list maxh. If, after some operations lifting height of the
current node becomes larger than the height of vertices in the list maxh, then we clear the list (sz = 0), and immediately
proceed to the next iteration of pushing (which will be built on the new list maxh).
int main () {
int n;
vector <vector <int>> c (n, vector <int> (n));
int s, t;
Reading ... n, c, s, t ...
vector <int> e
(n); vector <int>
h (n); h [s] = n-
1;
vector <vector <int>> f (n, vector <int> (n));
}
Finding the flow in a graph in which each
edge indicate the minimum and maximum
flow
Let a graph G, where each edge in addition to the capacity (maximum flow along this edge) and a minimum value
specified flow to pass along this edge.
Here we consider two problems: 1) the need to find an arbitrary stream of satisfying all constraints, and 2)
requires a minimum flow satisfying all constraints.
Solution of 1
We denote the minimum value of Li flow that can pass through the i-th edge, and by Ri - its
maximum value.
Made in the following graph changes. Add a new source S 'and stock T'. Consider all the edges, which Li is nonzero. Let i -
the number of such edges. Let the ends of the rib (oriented) - the vertices Ai and
Bi. Add an edge (S ', Bi), which L = 0, R = Li, add an edge (Ai, T'), in which L = 0, R = Li, and at most i-th
ribs put Ri = Ri - Li, and Li = 0. Finally, we add to the graph edge from T to S (old drain and source) that
L = 0, R = INF.
After these conversions all edges will have Li = 0, ie we reduced this problem to the usual
the problem of finding the maximum flow (but in a modified graph with the new source and the drain) (to
understand why the maximum - read the following explanation).
Correctness of these transformations is more difficult to understand. Informal explanation such. Each edge which Li is
nonzero, we replace two edges: one with a capacity of Li, and the other - with Ri-Li. We need
find a thread that would definitely satiated the first edge of the pair (ie, the flow along this edge must be equal to Li); second edge we
care less - flow along it can be anything, as long as it does not exceed
its bandwidth. So we need to find a thread that would definitely satiated a set of edges. Consider each such edge, and perform
such an operation: sum to its end edge
from the new source S ', sum up the edge of his start to the drain of T', remove the rib itself, and from the old to the old Photo T the
source S will hold an edge of infinite bandwidth. With these actions, we proimitiruem the fact that this edge is saturated - the rib will
follow Li flow units (we simulate it using a new source that
delivers at the end of the ribs desired flow amount), and will flow into it again Li flow units (but instead of ribs
this thread gets a new stock). Flow from the source of the new flows through one portion of the graph to the old dotekaet
Photo T, it flows from the source to the old S, then flows through another portion of the graph, and finally comes to the
beginning of this rib
and into the new sink T '. That is, if we find in this modified graph maximum flow (and the drain gets the right amount of flow, ie
the sum of all the values of Li - otherwise, the flux will be smaller and the response
simply does not exist), we simultaneously find the flow in the original graph, which will satisfy all the
minimum restrictions, and, of course, all the limitations of the maximum.
Solution of 2
Note that along the edge of the old drain in old source with bandwidth INF flows all the old stream, ie, the bandwidth of the
edge effect on the magnitude of the old stream. For sufficiently large values of the bandwidth of the edge (ie, INF) old flow is
not restricted. If we reduce the bandwidth, then, at some point, and will decrease the value of the old stream. But when
values are too small, the flux will be insufficient to ensure that the restrictions (the minimum value of the flow along the
edges). Obviously, you can apply binary search
meaningfully INF, and it is a lowest value, wherein all constraints have will be met, but the old
thread will have a minimum value.
Minimum cost flow (min-cost-flow).
Algorithm enhancing ways
Given network G, consisting of N vertices and M edges. Each edge (generally oriented, but on this occasion,
see below) contains the bandwidth (a positive integer), and the cost per unit of flow along this edge (an
integer). Column Set source S and drain T. gives some value K flux is required to find the flow of this
magnitude, and among all flows of this magnitude choose the stream with the lowest cost ("the task of min-
cost-flow").
Sometimes the task of putting a little differently: it is required to find the maximum flow (Least Cost
"problem min-cost-max-flow").
Both these problems are solved effectively increasing the algorithm described below ways.
Description
The algorithm is very similar to Edmonds-Karp algorithm for computing the maximum flow.
Define residual network for a fixed flow F as follows (actually, just as in the Ford-Fulkerson algorithm) residual network edge
belong only unsaturated (i.e. which Fij
<Uij), and the residual capacity of each such edge as UPIij = Uij - Fij.
Properly algorithm min-cost-flow is as follows. At each iteration, the algorithm finds the shortest path in the residual network
from S to T (the shortest relative cost Cij). If the path is not found,
then the algorithm returns, the thread F - desired. If the path has been found, we are increasing the flow along it as far
as possible (ie, pass along this path, we find the minimum residual bandwidth MIN_UPI edges of this path, and then
increase the flow along each edge of the path on the value MIN_UPI, do not forget to reduce the same amount of
reverse flow along the edges). If at any time the flux reaches the value K (given to us by the condition of the flux), we
also
stop the algorithm (note that if the last iteration of the algorithm when the flow along the path need to increase the flow by an
Easy to see that if we put K to infinity, the algorithm finds the minimum cost maximum flow, ie the same algorithm solves both
Implementation
Here is an implementation of the algorithm min-cost-flow, based on Leviticus algorithm.
Algorithm is applied to the input network (undirected multigraph) with N vertices and edges of M and K - the
quantity of flow that you want to find. The algorithm finds the flow of K minimum cost, if one exists. Otherwise, it
finds the maximum value of the flow minimum cost.
The program has a special feature to add an oriented edge. If need be
add undirected edge, then this function must be called for each edge (i, j) twice: from (i, j) and from (j, i).
struct rib {
int b, u, c, f;
size_t back;
};
void add_rib (vector <vector <rib>> & g, int a, int b, int u, int c)
{rib r1 = {b, u, c, 0, g [b]. size ()};
rib r2 = {a, 0,-c, 0, g [a]. size ()};
g [a]. push_back (r1);
g [b]. push_back (r2);
}
int main ()
{
int n, m, k;
vector <vector <rib>> g (n);
int s, t;
Graph reading ... ...
}
Assignment problem. Solution with a min-cost-
flow
The problem has two equivalent statement:
● Given a square matrix A [1 .. N, 1 .. N]. It is necessary to select N elements so that each row and column
has been selected exactly one element, and the sum of these elements is a minimum.
● There orders N and N machines. About every order known its manufacturing cost for each
machine. On each machine can perform only one order. Is required to distribute all orders for
the machines so as to minimize the total cost.
Here we consider the solution of the problem based on the algorithm finding the minimum cost flow
(Min-cost-flow), solving the problem of appointments for O (N5).
Description
Construct bipartite network: has a source S, runoff T, in the first part are N vertices (Corresponding rows of the matrix or
orders), the second - also N vertices (corresponding to the columns of a matrix or machines).
Between each vertex i of the first part and each vertex j of the second part will hold an edge with capacity 1 and the value of
Aij. From the source S will hold the edges to all vertices i of the first part with a capacity of 1 and
value of 0. From each vertex of the second part to the drain T j draw an edge with capacity 1 and cost 0.
Find in the resulting network maximum flow minimum cost. Obviously, the flux will be equal to N. Furthermore, it is obvious that for
each vertex i of the first part there is exactly one vertex j from the second part, such that the flow Fij = 1. Finally, it is obvious that
one-to-one correspondence between the vertices of the first part and
vertices of the second part is the solution of the problem (as found flow has a minimum value, the sum of the costs of the selected
Asymptotics of solutions of the assignment depends on how the algorithm searches for the maximum flow of
minimum cost. Asymptotics will O (N3) using Dijkstra's algorithm or O (N4) using the algorithm of Bellman-
Ford.
Implementation
Present here is the implementation longish possible, it can be reduced significantly.
int main ()
{
int n;
vvint a (n, vint (n));
Reading ... a ...
int m = n * 2 + 2;
vvint f (m, vint (m));
int s = m-2, t = m-1;
int cost = 0;
for (; ;)
{
vector <int> dist (m,
INF); vector <int> p (m);
vector <int> type (m, 2);
deque <int> q;
dist [s] = 0; p
[s] = -1; type
[s] = 1;
q.push_back (s);
for (;! q.empty ();)
{
int v = q.front (); q.pop_front
(); type [v] = 0;
if (v == s)
{
for (int i = 0; i <n; + +
i) if (f [s] [i]
== 0)
{
dist [i] = 0; p
[i] = s; type
[i] = 1;
q.push_back (i);
}
}
else
{
if (v <n)
{
for (int j = n; j <n + n; + + j)
if (f [v] [j] <1 && dist [j]
> Dist [v] + a [v] [j-n])
{
dist [j] = dist [v] + a
[V] [j-n];
p [j] = v;
if (type [j] ==
0) q.
push_front (j);
else if (type [j] == 2)
q.push_back (j);
type [j] = 1;
}
}
else
{
for (int j = 0; j <n; + + j)
if (f [v] [j] <0 && dist [j]
> Dist [v] - a [j] [v-n])
{
dist [j] = dist [v] - a
[J] [v-n];
p [j] = v;
if (type [j] ==
0) q.
push_front (j);
else if (type [j] == 2)
q.push_back (j);
type [j] = 1;
}
}
}
}
}
printf ("% d \ n",
cost); for (int i = 0;
i <n; + + i)
for (int j = 0; j <n; + + j)
if (f [i] [j + n] == 1)
printf ("% d", j +1);
}
Hungarian algorithm for solving the
assignment problem
● Given a matrix size . Required to find a permutation length That the quantity
- Minimal.
● Provides a complete bipartite graph with vertices; each edge is assigned some weight.
Want to find a perfect matching of minimum weight.
Note that the above statement "are square": They both dimensions are always the same (and equal
). In practice, often similar "rectangular"Setting when And should
select elements. However, as is easy to see from the "square" of the problem you can always go
to "square", adding rows / columns with zero / infinite values, respectively.
Also note that, by analogy with the search minimum solutions also pose the problem of search
maximum solutions. However, these two problems are equivalent to each other, all the weight is
enough to multiply .
Hungarian algorithm
Historical Background
The algorithm was developed and published by Harold Kuhn (Harold Kuhn) in 1955 gave himself Kun algorithm
called "Hungarian" because it was largely based on the earlier works of two Hungarian mathematicians: Denes
Koenig (Dénes Kőnig) and Eigen Egervary (Jenő Egerváry).
In 1957, James Mankres (James Munkres) showed that this algorithm works for the (strict) polynomial time (ie
the time of the order of a polynomial That does not depend on the magnitude of costs).
Therefore, in the literature, this algorithm is known not only as the "Hungarian", but as "algorithm
Kuna Mankresa" or "algorithm Mankresa."
However, recently (2006) revealed that the same algorithm was invented a century before the Kuna German
mathematician Carl Gustav Jacobi (Carl Gustav Jacobi). The fact that his work "About the research of the
order of a system of arbitrary ordinary differential equations", printed posthumously in 1890, contained, among
other results, and a polynomial algorithm for solving the assignment problem, was written in Latin, and its
publication gone unnoticed among mathematicians.
Also worth noting that the original algorithm Kuhn had asymptotic and only later Jack
Edmonds (Jack Edmonds) and Richard Carp (Richard Karp) (and, independently Tomidzava (Tomizawa))
shown how to improve its asymptotics .
(As can be seen, the number correspond to the lines, and the number
On the one hand, it is noted that the cost of the desired solution not less the value of any building:
(PROOF desired solution of the problem consists of a matrix of cells, and each of them
condition . Since all the elements are in different rows and columns,
then summing these inequalities for all selected In the left-hand side we obtain, and the right - ,
QED.)
On the other hand, it appears that there is always a solution and the potential at which this inequality
becomes an equality. The Hungarian algorithm is described below, will be
constructive proof of this fact. In the meantime, only pay attention to the fact that if a solution has a value equal to
the value of any potential, the decision - optimally.
Think of the alternative formulation of the assignment problem, using a bipartite graph. We denote
bipartite graph composed only of hard edges. In fact, the Hungarian algorithm support for the current building the
maximum number of edges on matching column : And
as soon as it becomes contain matching Ribs, ribs and this matching will be the desired
optimal solution.
Proceed directly to the description of the algorithm.
● At the beginning of the algorithm the potential is set to zero And matching relies empty.
● Further, at each step of the algorithm we try without changing the potential to increase the power of the
current matching per unit (recall is sought in the graph matching hard edges ).
This is actually used for ordinary Kuhn search algorithm maximum matchings in bipartite graphs.
Recall here the algorithm.
All edges of the matchings oriented in the direction from the first to the second part, all other
edges oriented in the opposite direction.
Recall (search matchings of terminology) that the vertex is called saturated if it is adjacent an edge of the current matching.
Vertex, which is not adjacent to any edge of the current matching is called unsaturated. Path of odd length, which does not
belong to the first edge matching, and for
all subsequent edges there is an alternation (belongs / does not belong) - called by magnifying.
Of all unsaturated vertices of the first part starts bypassing depth/across. If the bypass
managed to reach the top of the second unsaturated share, it means that we have found a way of increasing the share of the first
to the second. If procheredovat edges along this path (ie, the first rib include matching, eliminate second, the third turn, and so
on), we thereby increase the power matching unit.
If increasing the path was not, then this means that the current matching - Maximum in the
graph So in this case, proceed to the next step.
● If the current step is not able to increase output current matching, then made a conversion
capacity so that the next steps there are more opportunities for larger matchings.
We denote the set of vertices of the first part, which were visited traversal algorithm when trying to find Kuhn increasing the chain;
through - many visited vertices of the second part. We denote set of unsaturated vertices of the first part, through - The
set of unsaturated vertices of the second part.
Calculate the value :
(PROOF Suppose that . Then there is the hard edges , Wherein and .
This implies that the edge must have been directed from the first to the second part, i.e. this tough
. However, this is impossible, since we could not get
edge should be part of a matching into
saturated top Except passing along the edge of in . A contradiction, so .)
In addition, the old matching of hard edges can be left, i.e. matching all edges remain tight.
(PROOF To some stiff edge ceased to be tough as a result of changes
potential, it is necessary that the equality turned into inequality
. However, the left side could be reduced only in one case: when .
But times , It means that the edge could not be an edge matching, as required.)
Finally, to show that the potential change can not be infinitely, We note that for each such potential varies
the number of vertices reachable bypass, ie ,
strictly increasing. (This is not to say that an increasing number of hard edges.) (PROOF First, any node that was achievable,
attainable, and will remain. Indeed,
it is almost obvious if you go back to the definition of : In fact, if the minimum has been reached on the edge Then
this edge now becomes rigid, and therefore, the apex become achievable thanks to the edges and vertices .)
Thus, the total can not be more building conversions before being discovered
increasing the power chain and matching will be increased.
Thus, sooner or later be found potential, which corresponds to a perfect matching Being the
answer to the problem.
If we talk about asymptotics algorithm, it is because all must occur
increases matchings, before each of which occurs not more than building conversions, each
of which is carried out during .
For implementation we shall not give here, because it still does not get shorter than described
Construction of an algorithm
for ( )
Now learn how to implement the same algorithm for the asymptotic
behavior (For rectangular tasks
- ).
The key idea: we will now adding into consideration matrix row one after another,
а not treat them all at once. Thus, the algorithm described above will be:
● Add into consideration the next row of the matrix .
● Yet increasing the chain starting in this line, recalculate potential.
● As soon as the chain increases, alternate matching along it (thus including the last line of matching), and go to
the top (to the next line).
To achieve the required asymptotic behavior, it is necessary to implement steps 2-3 for each line
running matrix during (For rectangular tasks - for ).
To do this, we recall two facts proved above us:
● When the potential peaks that were achievable bypass Kuhn, achievable and will remain.
● Total could happen only building conversions before increasing the chain is found.
This implies key ideasThat achieve the desired asymptotic behavior:
● To check which increases chain no need to run again after bypassing Kuhn each conversion potential. Instead, you
can get a bypass Kuhn iterative form after each conversion potential we look to add a hard edge, and if they were left
ends
achievable, mark their right ends as achievable and keep bypassing of them.
● Developing this idea further, we can come to such a representation of the algorithm: a cycle, at each step, which is converted
first potential, then there is a column that has become achievable (and there will always be such as restated building there are
always new reachable vertices), and if this
unsaturated column was then found increasing the chain, and if the column was saturated - then the
corresponding row in the matching problem also becomes achievable.
The matrix is stored in -Indexing for convenience and brevity code. The fact that in this
implementation are introduced fictitious zero row and zero column that allows you to write many
cycles in general terms, without further checks.
Arrays and store potential. Initially, it is zero, which is true for the matrix consisting of zero lines. (Note that this
implementation is not important whether or not there in the matrix negative numbers.)
Array contains information about where these minima are obtained, we then
were able to restore magnifying chain. At first glance it seems that the array for each column,
line number to be stored, as well as make another array: for each row to remember the number of the column from
which we came into it. Instead, it can be seen that the algorithm Kuhn always gets in line, passing through the edge of
matching columns, so the line numbers to restore the chain can always be taken out of matchings (ie, from an array).
Thus for each column, contains
number preceding column (or If none).
The algorithm is a external loop through the rows of the matrixInside which
takes into consideration the addition of the second row of the matrix. The inner part is a series of "do-while (p [j0]!
= 0) ", which works, until it finds a free column . Each iteration of the marks
visited the new column with the number of (Counted on the last iteration and initially zero - ie
is, we start we dummy column), as well as a new line - Adjacent to it in matchings (ie ;
and initially at taken Th row). Because of the advent of a new row visited necessary
An
properly counted array , At the same time we find in it at least - value d
which column this minimum has been reached (note that in this implementation could be
zero, which means that the potential at the current step may not be changed: new attainable column has already).
After this conversion capacity, the corresponding change in the array.
When the cycle is "do-while" we found a magnifying chain terminating in a column, "roll"
which can, using an array of ancestors .
Constant - Is "infinity", i.e. some number greater than all known possible number of
input matrix .
Answer to reset a usual manner, i.e. finding for each row number of selected
it Column , Is as follows:
Cost found matching you can just take the potential of zero column (taken from
opposite sign). In fact, it is easy to trace through the code contains the sum of all
units Ie the total change in potential. While at each change of potential change could
several variables and , The total change in value of the potential is exactly equal ,
since there are no magnifying circuit, the number of rows achievable exactly one more than the number of
achievable column (only the current line has no "couples" as visited column):
Examples of problems
Here are a few examples in the task assignment: from the very trivial, and ending with less
obvious problems:
● Given bipartite graph, find it matching maximum minimum weight matching (I.e., primarily
maximized size matchings the second - the cost is minimized).
To solve the problem of building a simple assignment, putting in place the number of missing ribs "infinity".
After that we solve the problem by the Hungarian algorithm, and remove the ribs from the response of infinite weight (they
could enter
in response, if the problem has no solution in the form of a perfect matching).
● Given bipartite graph, find it matching maximum maximum weight
matching.
Decision again obvious, but all the weight must be multiplied by minus one (or in the Hungarian algorithm to
replace all the minima on the highs, and infinity - at minus infinity).
● Task detecting moving objects from images: Was made two shots,
following which there were two set of coordinates. Required to relate the objects in the first and second
picture, ie, to determine for each point of the second shot, the first shot at what it was adequate. It is required
to minimize the sum of the distances between the mapped points (ie, we seek a solution in which objects are
passed smallest total path).
To solve we just build and solve the problem of assignment, where the weights of the
edges are the Euclidean distance between points.
● Task detecting moving objects on the radar: There are two locator who can not determine the position of an object in
space, but only the direction to it. With both locators (at different points) came in the form of information such
directions. Required to determine the position of objects, ie determine the expected position of objects and their
corresponding pairs of directions so as to minimize the sum of distances from objects to ray-directions.
Decision - again, just build and solve the problem of assignment, where the vertices of the first part are directions from
the first locator vertices of the second part - directions from the second locator and edge weights
- The distance between the beams.
● Cover directed acyclic graph paths: A directed acyclic graph, find the smallest number of paths
(with equal - with the smallest total weight) to each vertex of the graph would lie in exactly one way.
Solution - build on the graph corresponding bipartite graph, and find the maximal matching of
minimum weight. For more details see separate article.
● Coloring wood. Given a tree, wherein each vertex, but leaves, has exactly
sons. You want to select for each vertex of some color colors so that no two adjacent vertices have the same color.
Furthermore, for each vertex of each color and known dyeing this peak value in the color and required to minimize
the total cost.
Solutions for use by dynamic programming. Namely, learn to consider the value
Whe
rein - Number of vertices - number of colors, and the value - This is the minimum cost
Coloring vertices together with its descendants, the vertex itself has color. To calculate this
, It is necessary to allocate the
value remaining flowers on top of the sons, and for this it is necessary to build
and solve the problem of assignment (in which the vertices of one share - the colors, the other vertices share - tops-sons,
and weights of the edges - This value corresponds to the
speaker ).
Thus, each value considered by solving the assignment problem, with the result that
gives an asymptotic .
● If the assignment problem is not given weight of the ribs, and at the tops, and only at the tops of one
share, It is possible to dispense with the Hungarian algorithm, but rather a sort weight vertices and
start normal Kuhn algorithm (See more details separate article).
● Consider the following special case. Let each vertex of the first part is attributed to a number of ,
. Suppose that the weight
and each vertex of the second part - of any edge equal (Number and
we know). Solve the problem of appointments.
For solutions without the Hungarian algorithm, we first consider the case in which both shares two vertices. In this
case, as is easily seen, it is advantageous to connect the vertices in reverse order: the top with less join
. This rule can be easily generalized to an arbitrary number of vertices: it is
a vertex with larger necessary
sort the vertices of the first part in order of increasing , The second part - in order of decreasing ,
and connecting the vertices in pairs in that order. Thus, we obtain a solution with the asymptotic .
● The problem of the potentials. Given a matrix . Required to find the two arrays
and such that, for any and running But the sum of
an
array elements d maximum.
Knowing the Hungarian algorithm, this task will not be difficult: the Hungarian algorithm just finds
Which satisfies the condition of the problem. On the other hand, without
it is so much potential knowledge
Hungarian algorithm to solve this problem seems almost impossible.
Literature
● Ravindra Ahuja, Thomas Magnanti, James Orlin. Network Flows [1993]
● Harold Kuhn. The Hungarian Method for the Assignment Problem [1955]
where by denotes the set of all edges And through - Weight of the edge .
Required to find cut the minimum weight.
Sometimes this problem is called a "global minimum cut" - in contrast to the task when given the vertex and the
source-drain and requires a minimal incision containing runoff and does not contain the source.
Global minimal incision equal to the minimum of the minimum cost cuts over all pairs source-drain.
Although this problem can be solved by using an algorithm for finding the maximum flow (running it time
for all pairs of source and drain), but the following describes a much simpler and faster algorithm proposed Matilda
Stohr (Mechthild Stoer) and Frank Wagner (Frank Wagner) in 1994
Generally allowed loops and multiple edges, although, of course, hinges absolutely no influence on the result,
and all multiple edges can be replaced by a single edge with their total weight. Therefore, for simplicity, we shall
assume that the input graph loops and multiple edges are missing.
(Ie, the maximum sum of the weights of edges, one end of which And the other belongs ).
Again, this process is completed through iteration, when all the vertices will move in many (Incidentally, this
process is very similar Prim's algorithm). Then, as the Stohr-Wagner theorem,
if we denote by and Recently the two written in peaks, the minimum cut between vertices and will consist of a single
vertex - . The proof of this theorem will be given in the next section (as is often the case, by itself it does not contribute to the
understanding of the algorithm).
Thus, total chart Curtains-Wagner is. The algorithm consists of phase. Each
phase set relies first consisting of a vertex; counted starting weight
vertices . Then there iteration, each of which is selected vertex with
highest value and added to the set , Then converted values
for the remaining vertices (which, obviously, need to go through all the edges of the selected vertex adjacency list
). After all the iterations we remember in and the numbers of the last two vertices added, and
As the cost of minimal cut found between and can take the value . Then
found to be compared with the current minimum cut answer if less, then update the answer. Go
to the next phase.
If you do not use any complex data structures, the most critical part is to find the vertex
with the highest value. If you make it for Then, given that all phases And by iteration
each, total asymptotic behavior of the algorithm obtained .
If finding the top with the highest value use Fibonacci heap
(Which allow to increase the value of the key for in the middle and make the most for on average)
all operations related to a plurality of executed on one phase for . Summary
asymptotic behavior of the algorithm in this case is .
Proof Stohr-Wagner
Recall the condition of this theorem. If we add to the set turns all the vertices, each time adding a vertex, most strongly
associated with this set, then we added the penultimate summit via And last - through . Then the minimal - incision
consists of a single vertex - .
To prove this, consider an arbitrary - incision and show that its weight can not be less than the weight
section, consisting of a single vertex :
For this we prove the following fact. Let - Set the state immediately before the addition of
top. Let - Cut set Induced by a cut (in other words,
equal to the intersection of these two sets of vertices). Next, the top called an active (with respect to the slit) when the
vertex and added to the previous vertex belong to different parts of the section.
Then, it is claimed for any active vertices the inequality:
So, we will prove the inequality, for which we use the method of mathematical induction.
For the first active vertex this inequality is true (in fact, it becomes an equality) - because all the
vertices belong to the same part of the section, and - Other.
Suppose now that this inequality holds for all active vertices until some vertex Prove it for the next active
vertex . To do this, transform the left side:
- This follows from the fact that when a plurality of power was , It was just added top, and not,
so she had the highest value.
Hence we have:
Now, note that the top and all vertices are different parts of the section, so this
denotes the sum of weights of the edges, which are
value included in But has not yet been taken into account
in , Which yields:
QED.
We have proved the relation And from there, as mentioned above, it should be and all theorem.
Implementation
For the most simple and clear implementation (with the
asymptotic ) Was chosen as a graph representation
adjacency matrix. The answer is stored in the variable and (The desired value of the minimum
cut themselves vertices contained in it).
For each vertex in the array stored, whether it exists or it was associated with some
other vertex. The list for each vertex compressed numbers stored source vertices that have been compressed
the vertex.
The algorithm consists
of phase (in the variable cycle ). At each phase of the first all vertices are
filled with zeros, and the
outside the set, for which the array connectedness all vertices are zero. Each
of iteration is the summit with the highest value . If this is the last iteration, the answer
if necessary, updated, and the penultimate and the last selected vertices are merged into one.
If not the last iteration, the
Setting objectives
Let - net (network), that is a directed graph in which the selected vertex-source and runoff.
Denote the set of vertices, the set of edges - through . Each edge compared
the graph does
its capacity and the cost per unit flow . If some ribs not,
it is assumed that .
Flow (Flow) network called such a real-valued function which assigns to each pair
vertices flow between them, and satisfies the following three conditions:
The problem of finding minimal cost flow is that for a given volume flow is required to find a stream with a
minimum cost. Should pay attention to the fact
that the value assigned to the edges are responsible for the cost per unit of flow along this edge; problem
sometimes occurs when the edges are mapped value stream flow along this edge (ie, if the flow proceeds of
any size, then this cost will be charged, regardless of the value stream) - this problem has nothing to do with
being considered here and, moreover, is NP-complete.
The problem of finding maximum flow of minimum cost is
to find the flow of the highest value, and all of these - with minimal cost. In the special case when the weights of all edges
are the same, the problem becomes equivalent to the usual problem of maximum flow.
The problem of finding minimum cost circulation is to find a value of zero flow to a minimum value. If all non-negative
value, then, of course, the answer is zero flow; if there is a negative edge weights (or rather, the weight of the negative
cycle), even
at zero flow stream may find a negative value. The problem of finding a minimum cost circulation can, of course, and put on the
network without the source and drain, as no semantic load they carry (however, in this graph, you can add the source and drain
in the form of isolated vertices and
get a regular on the wording of the task). Sometimes the task of finding the maximum value of the circulation
- Clear enough to change the value of the ribs on the opposite and get the problem of finding a
minimum cost circulation already.
All these problems, of course, can be extended to undirected graphs. However, the move from
undirected graph to easily oriented: each undirected edge with
a
n
Bandwidth and cost should be replaced by two directed edges d
with the same bandwidth and cost.
Residual Network
Concept residual network based on the following simple idea. Suppose there is some flow ;
along each edge takes some flow . Then along this edge can be
(Theoretically) to put more flow units; this value and call residual
Bandwidth:
In the residual
However, apart from this, direct ribs network appears and back edge
. Intuitive sense of the edge that we can in the future to cancel part of the stream that flowed through the
edge . Accordingly, the flow passing along the edges of the reverse actually, formally,
means a decrease in the flow along the edge . Back edge has a bandwidth equal to zero
(To, for example, and back edges would have been impossible to miss the stream; at
positive value reverse edges to become the property of the antisymmetry That
less Ie you can skip some return flow along the edges), the residual
capacity - equal to flow along a straight edge, and cost - the opposite (because after the cancellation of the
flow, we must be reduced accordingly and the price):
Thus, each oriented edge corresponds to two oriented edges in the residual network And each edge of the residual
network, an additional characteristic - residual
bandwidth. However, you will notice that the expressions for residual capacity by
essentially the same for both the forward and reverse edges, ie we can write for any edge
residual network:
By the way, the implementation of this property prevents storing residual capacity, and simply calculate
them when needed for an edge.
It should be noted that the residual network removes all edges with zero residual capacity
ability. Residual Network must be only edges with positive
residual bandwidth .
Here you should pay attention to this important point: if the network were simultaneously both edges and ,
then the residual network, each of them will appear on the reverse side, and the result will multiple edges.
For example, this situation often occurs when the network is built on an undirected graph (and, it turns out
each undirected edge will eventually lead to the emergence of four edges in the residual network). This feature
must always be remembered, it leads to a slight complication of programming, although in general does not change anything.
Furthermore, the designation rib in this case becomes ambiguous, so here we shall everywhere
assume that the situation in the network is not (solely for the purpose of simplicity and correctness of
descriptions, and on the correctness of the ideas is not affected).
flow is reduced (reduced by the cost of the cycle, multiplied by). Thus, if there is a cycle of negative weight, may not be
optimal, QED
Proof: The sufficiency. To do this, we first prove some auxiliary facts.
Lemma 1 (Decomposition of the stream) any flow can be represented as a set of paths from the source to the drain
and cycles, all - have a positive flow. Prove this lemma is constructive: we show how to break into a plurality of flow paths
and cycles. If the thread has a nonzero value, then obviously, the source of out at least one edge with positive flow;
pass along this edge, find ourselves in some vertex . If this vertex, then stop - have found a way of in. Otherwise,
according to the property to maintain the flow of
must extend at least one edge with positive flow; Go through it at some vertex . Repeating this process, we will either
come in stock , Or else come to some vertex a second time. In the first case we find a way of to, in the second - cycle.
Found path / cycle will have a positive flow (minimum flow of the edges of this path / cycle). Then reduce the flow along
each edge of the path / cycle by an amount due to get re-flow, which again apply this process. Sooner or later, all the
edges along the stream becomes zero, and we find him on the way and decomposition cycles.
Lemma 2 (On the difference between the fluxes) For any two values of flow and
a( ) Flow can be
. Indeed, consider the difference between
represented as a stream, plus a few cycles in the residual network these
flows (Subtraction flows - this term by term subtraction, ie subtraction flows along each edge).
It is easily seen that the result will be some flow zero value, ie circulation. We make the decomposition of this circulation by the
previous lemma. Obviously, it can not be decomposed paths (because the presence - Path positive flow means that the
quantity of flow in the network is positive). Thus, the difference in flow and can be represented as the sum of network
cycles .
This asymptotic behavior - not strictly polynomial (strong polynomial), because it depends on amount of
bandwidth and cost.
However, if the search is not an arbitrary negative cycle, and use some kind of a clear approach, the asymptotic
behavior can be significantly reduced. For example, if every time look for a cycle with minimum average cost (which
can also be adjusted for), while the work of the entire algorithm is reduced
Implementation
We first introduce the data structures and functions to store the graph. Each edge is stored in a separate structure
All edges lie in the general list And for each vertex numbers are stored in the vector
edges emanating from it. This organization makes it easy to find a number, reverse rib line straight edges - they are in
the list of neighbors, and the number one can be obtained by calling another
operation "^ 1" (it inverts the LSB). Adding oriented edge in the graph performs the function
Which immediately adds forward and reverse edges.
In the main program after reading the graph is an infinite loop, inside which runs the algorithm of Bellman-Ford, and if it
detects a negative cost cycle, then this cycle along the flow increases. As the residual network may be a disconnected
graph, the algorithm runs the Bellman-Ford from each not yet reached the top. In order to optimize the algorithm uses
the queue (the current queue and the new queue ) So as not to touch on all the edges of each step. Detected
along the cycle each time the unit is pushed exactly flow, although, of course, in order to optimize the flow quantity can
be defined as the minimum residual capacity.
if (! Found) break;
}
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
● Ravindra Ahuja, Thomas Magnanti, James Orlin. Network flows [1993]
● Andrew Goldberg, Robert Tarjan. Finding Minimum-Cost Circulations by Cancelling
A little history
This algorithm was published by the Soviet (Israeli) scientists Yefim Diniz (Yefim Dinic, sometimes
written as "Dinitz") in 1970, ie even two years before the publication of the Edmonds-Karp algorithm
(however, both algorithms were independently discovered in 1968).
Furthermore, it should be noted that some of the algorithm were made to simplify Shimon Ewen (Shimon
Even) and his student Alon Itai (Alon Itai) in 1979 of Because of them got his algorithm modern look: they
used to the idea of the concept of blocking flows Diniz Karzanova Alexander (Alexander Karzanov, 1974)
and reformulated the algorithm to the combination of bypass in width and in depth, which is now the
algorithm and presents everywhere.
Development of ideas in relation to streaming algorithms extremely interesting to consider, taking into account "Iron
Curtain" of those years, divided the USSR and the West. Evident sometimes similar ideas were almost simultaneously
(as in the case of the algorithm Diniz and Edmonds-Karp algorithm), however, while having different efficiencies
(algorithm Diniz one order of magnitude faster); sometimes, on the contrary, the emergence of ideas on one side
"curtain" ahead of a similar move on the other side more than a decade (the algorithm Karzanova push in 1974, and the
algorithm of Goldberg (Goldberg) push in 1985).
Necessary definitions
We introduce three definitions needed (each of them is independent from the others), which are
then used in the algorithm Diniz.
Residual network with respect to the network Some streams and it is called a network in which each edge
a bandwidth and stream correspond to two edges
● a bandwidth
● a bandwidth
It is noteworthy that with this definition in the remaining network may appear multiple edges: if the
original network were as rib And .
Residual edge can be intuitively understood as a measure of how still possible to increase the flow along some
ribs. In fact, if an edge a bandwidth stream flows Then potentially
You can skip over it yet units of flow and in the opposite direction to be skipped
flow units, which would mean flux cancellation in the initial direction.
Blocking flow in this network is called a torrent that any path from the source in stock contains
saturated this stream edge. In other words, the network can not find such a path from the source to
the drain, along which you can easily increase the flow.
Blocking flow is not necessarily maximal. Ford-Fulkerson theorem says that the flow will be maximal if and only if in
the residual network is not there the way; in blocking same thread says nothing about the existence of the path
along the edges that appear in the residual network.
Layered network for the network is constructed as follows. First the length of the shortest paths
from the source to all other vertices; call level top of its distance from the source. Then
layered network include all those edges the source network, which lead from one level to another,
(Why in this case the difference of distances is
later, the level, i.e. not
can exceed unity follows from the properties of the shortest distances). Thus, all the edges are removed, located entirely
within the levels, as well as the edges leading back to previous levels.
Obviously, a layered network is acyclic. In addition, any path in a layered network is the shortest path
in the home network.
Build a layered network of the network is very easy: you need to run wide detour along the edges of the network, thereby
considering each vertex value And then make a layered network is suitable ribs.
Note. The term "layered network" in Russian literature is not used; this design is usually referred to simply as "auxiliary
graph." However, the English term commonly used
"Layered network".
Algorithm
Diagram
The algorithm consists of several phases. Each phase is constructed first, the residual network, and then in relation to it being
built layered network (preorder traversal), and it sought an arbitrary blocking flow.
Found blocking the flow added to the current flow, and that the next iteration ends.
This algorithm is similar to the Edmonds-Karp algorithm, but the main difference can be understood as follows: at
each iteration, the flow does not increase along one of the shortest path, and along the whole set of such
paths (because it is in such ways are the ways in blocking the flow of a layered network).
where the subscript denotes the number of phases, which are taken to the values of these variables.
Proof. Fix an arbitrary phase and an arbitrary vertex and consider any shortest path Online (recall
so we denote the residual network, taken before
Note that in the residual network may include only the edges of the And ribs, the ribs of the inverse
(This follows from the definition of the residual network). Consider two cases:
contains only the edges of . Then, of course, the path
● Path the length greater than or equal to (Because
that by definition - the length of the shortest path), which means that the inequality.
● Path comprises at least one rib that is not contained in the (But the opposite edge of some ).
Consider the first such edge; let it be an edge .
We can apply our lemma to the top, because it falls under the first case; So we
we obtain .
The second
Now, note that since the rib appeared in the residual network only after phase,
it follows that along the edge was further omitted some thread; Consequently, the ribs
belonged to a layered network before the second phase, and
therefore . Consider that
property on the shortest paths And combining this with two
previous inequalities, we obtain:
Now we can apply the same reasoning to the remaining path to (Ie, that each adds to the inverted rib at least
two), and finally obtain the required inequality.
Lemma 2. The distance between the source and the drain increases strongly after each phase of the algorithm, ie:
where the prime marked value obtained on the next phase of the algorithm.
Proof: by contradiction. Assume that after the current phase appeared, that. Consider the shortest path
from the source to the drain; by assumption, its length
should remain unchanged. However, the residual network for the next phase contains only the edges of the
residual network before executing the current phase or reverse them. Thus, a contradiction: found a
path that does not contain saturated edges and has the same length as the shortest path. This path should
be "locked" blocking the flow of what had happened, and what is a contradiction, as required.
This lemma can be understood intuitively as follows: on The second phase of the algorithm
identifies and Diniz pervades all path length .
Since the length of the shortest path from in can not exceed Then, consequently,
the algorithm performs Dinitz max phase.
case). One can understand that one start crawling into the depths of the main program runs for Where -
by advances pointers. Given that only runs deep traversal within one search
blocking flow will Where - the number of edges, it blocks the flow of saturated, then the whole
That, given that all the pointers in the amount
search algorithm for blocking the flow of exhaust of
gone the distance , Gives an asymptotic . In the worst case, when the blocking flow saturates
all edges asymptotics obtained ; this asymptotic behavior and will be used on.
We can say that this method of finding a blocking flow is extremely effective in the sense that increasing the search one
way he spends an average of operations. Therein lies the difference on
an order of effectiveness of the algorithm Diniz and Edmonds-Karp (who is looking for a way for
increasing ).
This method of solution is still simple to implement, but effective enough, and therefore most
often used in practice.
● You can apply a special data structure - dynamic Sletora trees (Sleator) and Tarjan (Tarjan)). Then
each blocking flow can be found in time .
Asymptotics
Thus, the entire algorithm runs in Diniz If blocking the flow of search described
the above method
for . Implementation using dynamic trees Sletora Tarjan and will work
during .
Single network
Unit called such a network in which all capacities are equal to 0 or 1, and each vertex either
incoming or outgoing edge only.
This case is important enough, because the problem of finding maximum matchings
network is built exactly the unit.
We prove that the algorithm for single networks Diniz even a simple implementation (which is an
arbitrary graph fulfills for) works in time, reaching to the problem of finding
maximum matching of the best known algorithms - Hopcroft-Karp algorithm. To prove
this assessment, it is necessary to consider
two cases:
● If the value does not exceed the desired flow .
In this case, we find that the number of phases and starts crawling in depth is the value . Recalling that
one phase in this embodiment operates in Obtain final
asymptotics .
Implementation
We present two implementations of the algorithm for Running on the network, given the adjacency matrices and
adjacency lists, respectively.
bool bfs() {
int qh =0, Qt
=0; q[qt + +] =
S;
memset (d, -1, N * sizeof d[0]);
d[s] = 0;
while (qh <qt) {
int v = q[qh + +];
for (int to =0; to <n; + + To)
if (d[to] == -1 && F[v] [to] <C[v] [to])
{ q[qt + +] = To;
d[to] = D[v] + 1;
}
}
return d[t] , = -1;
}
int dinic() {
int flow = 0;
for (,;) {
if (! Bfs()) break;
memset (ptr, 0, N * sizeof ptr[0]);
while (int pushed = dfs (s, INF))
flow + = pushed;
}
return flow;
}
The network must be pre-read: must be specified variables ,,, As well as read
. The main function of the decision
matrix of capacities - Which returns
found the maximum flow value.
struct edge {
int a, b, cap, flow;
};
bool bfs() {
int qh =0, Qt
=0; q[qt + +] =
S;
memset (d, -1, N * sizeof d[0]);
d[s] = 0;
while (qh <qt && d[t] == -1) {
int v = q[qh + +];
for (size_t i =0; i <g[v]. Size(); + +
I) { int id = g[v] [i],
to = e[id]. B;
if (d[to] == -1 && E[id]. Flow <e[id]. Cap)
{ q[qt + +] = To;
d[to] = D[v] + 1;
}
}
}
return d[t] , = -1;
}
int dinic() {
int flow = 0;
for (,;) {
if (! Bfs()) break;
memset (ptr, 0, N * sizeof ptr[0]);
while (int pushed = dfs (s, INF))
flow + = pushed;
}
return flow;
}
The network must be pre-read: must be specified variables , , And adding all
edges (oriented) using function calls . The main function of the decision - ,
which returns the value of the maximum flow found.
Kuhn's algorithm for finding the greatest matching in
a bipartite graph
Dan bipartite graph containing and peaks Ribs. Required to find a maximum matching, ie
select as many edges to none selected edge had no common vertex with any other selected edge.
Necessary definitions
A matching is a set of pairwise non-adjacent edges of the graph (in other words, any vertex of the graph
must be incident to at most one edge of the set ). Power matching call number of edges in it. The greatest (or
maximum) matching is called matching, whose power is maximum among all possible matchings in this graph. All
those vertices that have adjacent edges of matchings (ie, which have exactly one degree in the subgraph formed
), This is called a matching saturated.
Chain length called some simple way (I.e., does not contain duplicate vertices or edges)
comprising Ribs.
Alternating chain (In the bipartite graph, with respect to some matching) call chain, in ribs which in
turn owns / not belong to matching.
Increasing the chain (In the bipartite graph, with respect to some matching) call alternating chain, whose
initial and final vertices do not belong to matching.
Berge's theorem
Formulation. Matching the maximum if and only if When there is no increasing chain
relative thereto.
Proof of necessity. Show if matching maximum, then there is a relatively magnifying circuit. This proof will be
constructive: we show how to increase by this magnifying circuit Power matchings unit.
To do this, perform the so-called alternating matchings along the chain . We remember that, by definition, the first edge of
the path does not belong to matching, the second - belongs to the third - again does not belong to the fourth - owned, etc.
Let's change the status of all edges along the chain: those edges that are not included
в matching (the first, third and so on until the last) are included in the matching, and the edges that were previously
в matching (the second, fourth and so on until the penultimate) - remove from him.
It is clear that the power of matching thus increased by one (because it was added to one edge more than deleted). It remains to
verify that we have built a correct matching, ie that no vertex has no right two adjacent edges of this matching. For all vertices
alternating chain Except the first and last, it follows from the alternation of the algorithm: first, we have removed the top of each
such adjacent edges, then added. For the first and last vertex chain and nothing could
broken as to interleave they had to be unsaturated. Finally, for all other vertices
- Non-chain - Obviously, nothing has changed. Thus, we actually built a matching, and one greater power than the old one,
which completes the proof of necessity.
The proof of sufficiency. We prove if under some matchings No increase the ways it - the
maximum.
Proof by contradiction. Suppose there is a matching having more power than
. Consider the symmetric difference of these two matchings, ie leave all the edges included in or
, But not both simultaneously.
It is clear that the set of edges - is certainly not matching. Consider what kind of a set of edges
has; for convenience we will treat it as a graph. In this graph, each vertex obviously has degree 2
(because each node can have a maximum of two adjacent edges - one matchings and from another).
Easy to understand that if this graph consists only of cycles or paths, and neither one nor the other do
not intersect with each other.
Now, note that the way in this column may not be any, but only even length. In fact, any path in the graph alternate ribs: the
ribs of the post edge goes from And vice versa. Now, if we look at some way of odd length in the graph , It turns out
that in the original graph it will increase the chain or
for matchings , Or for . But this could not be, because in the case of matching this contradicts
with the proviso in the
case - At its maximum (in fact we have already proved the necessity of the theorem from which
it follows that the existence of increasing the matching circuit can not be maximal).
We now prove the analogous statement for cycles and all cycles in the graph can only have an even length.
It is quite easy to prove: it is clear that the cycle edges also must alternate (owned by turns , That is), but this
condition can not be executed in a cycle of odd length - it sure there are two adjacent edges of a matching, which
contradicts the definition matching.
Thus, all the paths and cycles of are of even length. Therefore, the graph
and
contains an equal number of edges of of . But given that
contains all the edges and ,
a
n
except for their common edges, it follows that the power d coincide. We have a contradiction:
by assumption, matching was not the maximum, then the theorem is proved.
Kuhn algorithm
Kuhn's algorithm - the direct application of the theorem of Berge. It can be briefly described as follows: first, take the
empty matching, and then - until the graph can not find magnifying chain - will perform alternating matchings along the
chain, and repeat the process increases the search chain. Once such a chain can not find - the process stops - the
current matching is maximum.
You're finding a way to increase the detail circuits. Kuhn algorithm - Just looking for any of these circuits using traversal
depth or across. Kuhn's algorithm looks at all the vertices
graph in turn, starting from each tour, trying to find a magnifying circuit, starting at this vertex.
Convenient to describe the algorithm, assuming that the graph is split into two parts (though in fact the algorithm
can be implemented and so that he was not given the input graph is clearly divided into two lobes).
The algorithm looks at all the vertices the first part of the graph:. If the current vertex current matching is already
saturated (ie already selected some edge adjacent to it), then skip this summit. Otherwise
- The algorithm tries to satiate this summit, which starts searching magnifying chain, starting with
this vertex.
Search magnifying circuit by means of a special bypass in depth or width (usually for ease of implementation is used
to bypass the depth). Originally worth a detour to the depth in
current unsaturated top the first part. We examine all the edges of the vertex, let the current edge -
this edge . If the top matching is not yet saturated, it means that we were able to find
magnifying circuit: it consists of a single edge ; in this case, simply turn this edge
already saturated with
to stop and search matching magnifying chain of peaks. Otherwise - if the how-
then edge , Then try to pass along this edge: thus we will try to find a magnifying
chain extending through the fins , . To do this, simply go to our crawl to the top - now
we try to find the magnifying circuit from this vertex.
One can understand that as a result of this tour, running from the top Or will magnifying circuit and
thereby saturate top Or as a magnifying circuit not find (and, hence, this peak is not able to
become saturated).
Once all vertices will be reviewed, the current matching is maximized.
Operation time
Thus, the algorithm can be represented as HRK series of starts crawling depth / width of all column. Consequently,
only the algorithm is executed during That in the worst case, there is .
However, this estimate may be a little improve. It turns out that it is important for the algorithm Kuhn what portion is
selected for the first, and which - in the second. In fact, in the above implementation starts crawling depth /
Whe
width of the peaks occur only the first part, so the whole algorithm is executed during rein
(Wh - Number of vertices of
- The number of vertices of the first part. In the worst case, it is erein the second
share). This shows that more profitable when the first fraction contains fewer vertices than the
second. Very unbalanced graphs (when and differ), this translates into a significant
difference at work.
Implementation
We present here the implementation of the above algorithm, based on a detour into the depths,
and the host in the form of a bipartite graph clearly broken into two parts of the graph. This
implementation is very concise, and perhaps it is worth remembering in this form.
Here - The number of vertices in the first part, - In the second part - from the top of a list of edges the first part (ie,
a list of numbers of vertices that are of these edges ). Vertices in both lobes are numbered
independently, ie first share - with numbers , The second - with numbers .
Then there are two auxiliary array: and. First - - Contains information about the current matching. For
programming convenience, this information is contained in only the second for the vertices
- Is the number of vertices of the first part, associated with the top edge of the second part
share: (or When
no edges of matchings not in). The second array - - The usual array of "visit" vertices
Bypassed in depth (need it just to bypass the depth does not extend into one vertex twice).
Function - And there is a detour to the depth. It returns If she could find magnifying circuit
from the top When it is considered that this function has already made alternate matchings found along the chain.
Inside the function scans all the edges emanating from the vertex the first part, and then checks if it
unsaturated rib leads into the top, or if the vertex saturated, but can not find the magnifying
recursive chain starting from , Then we say that we found the magnifying circuit and before returning
with the result of the function produce alternating current in Fin redirect edge adjacent to,
on top.
In the main program first indicates that the current matching - empty (list filled with numbers
). Then moves the vertex of the first part of it and started crawling in depth ,
pre-zeroed array .
It should be noted that the size of matchings easily get the number of calls in the main
program who returned result . Deposit required maximal matching in the array .
int n, k;
vector <vector <int>> G;
vector <int> Mt; vector
<char> Used;
bool try_kuhn (int v) {
if (used[v]) return false;
used[v] = true;
for (size_t i =0; i <g[v]. Size(); + +
I) { int to = g[v] [i];
if (mt[to] == -1 | | Try_kuhn (mt[to])) {
mt[to] = V;
return true;
}
}
return false;
}
int main() {
Graph reading ... ...
Once again, the algorithm is easy to implement and Kuhn so that he worked on graphs for which we know that they
are bipartite, but their clear division into two lobes found. In this case it is necessary to abandon the convenient
partition into two parts, and all the information stored for all vertices. For this array of lists now set not only for the
vertices of the first part, but for all the vertices of the graph (of course, now tops both lobes are numbered in total
numbering - from to ). Arrays and now also defined for the vertices of both shares and, therefore, they must
be maintained in this state.
An improved
We modify the algorithm as follows. Prior to the main loop of the algorithm will find some simple algorithm arbitrary
matching (Simple heuristic algorithm), And only then will perform a cycle with function calls kuhn (), which will
improve this matching. In
, the algorithm will run much faster on random graphs - because in most graphs can easily dial matching sufficiently
large weight through heuristics, and then improve to the maximum found matching algorithm has the usual Kuhn. Thus
we will save on
starts crawling depth of the vertices that we include with the heuristics in the current matching.
For example, you can just iterate through all the vertices of the first part and for each of them to
find any edge that can be added to matching, and add it. Even such a simple heuristic algorithm
is able to accelerate Kuhn several times.
It should be noted that the main loop will have to slightly modify. Since the function call in the main loop is assumed that
the current node is not yet included in the matching, then
need to add an appropriate test.
In implementing change only the code in the function main ():
int main() {
Graph reading ... ...
Another good heuristics is the following. At each step, you will seek the top least likely (but not isolated), choose
any of her edge and add it to the matching,
then removing both these vertices with all their incident edges of the graph. Such greed works very well for random graphs,
even in most cases builds a maximal matching (though against her there is a test in which it finds a matching value
significantly lower than the maximum).
Checking graph on bipartition and split into two
parts
Suppose we are given an undirected graph. Want to check whether it is bipartite, ie whether it is
possible to divide it into two parts, the vertices so that there are no edges connecting two vertices
of one share. If the graph is bipartite, then withdraw themselves share.
Solve this problem by using search width in O (M).
Sign dipartition
Theorem. A graph is bipartite if and only if all its simple cycles are of even length.
However, from a practical point of view to look all simple cycles uncomfortable. Much easier to
test graph on bipartition the following algorithm:
Algorithm
We carry out a series of searches in width. Ie will run breadth-first search from each unvisited vertex. That vertex from
which we start walking, we put in the first part. In searching the width, if we go to some new vertex, then we put it in a
fraction different from the current share of the top. If we try to pass on to the top edge, which has already had, then we
check to the vertex and the current vertex were in different proportions. Otherwise, the graph is not bipartite.
At the end of the algorithm, we either find that the graph is not bipartite, or find a partition of vertices into
two parts.
Implementation
int n;
vector <vector <int>> g;
Graph reading ... ...
Algorithm
The algorithm is extremely simple. Sort the top of the first part in descending order (more precisely,
non-increasing) weights, and apply to the resulting graph Kuhn algorithm.
It is alleged that obtained with the maximum (in terms of number of ribs) and the matching is
optimal in terms of sums of weights saturated vertices (despite the fact that after sorting, we do
not actually use these weights).
Thus, the implementation will be something like this:
int n;
vector <vector <int>> g (n);
vector used (n);
vector <int> order (n); / / Vertex list, sorted by weight
Reading ... ...
Proof
Recall the basic provisions matroid theory.
Matroid M - is an ordered pair (S, I), where S - a set, I - a non-empty family of subsets of S, which satisfy
the following conditions:
Called the weighted matroid if for every element xS defined on some weight. Weight
subset is the sum of the weights of its elements.
Finally, the most important theorem in the theory of weighted matroid: to get the best response, ie independent
subset with the largest weight, you need to act greedily, starting with the empty subset, we add (unless, of
course, the current item can be added without violating the independence) all the elements one by one in order
of decreasing (or rather, non-increasing) their weights:
It is alleged that at the end of this process we obtain a subset with the largest weight. Now
proveThat our task - neither more nor less than the weighted matroid.
Let S - set of all vertices of the first part. To keep our task in a bipartite graph with respect to matroid vertices of the first
Then the problem of finding the maximum weight matchings now be reformulated as a problem of
finding the maximum weight independent subset.
It remains to verify that the following three conditions above imposed on matroid. Firstly, it is obvious that S is finite.
Secondly, it is obvious that the removal edges of the removal is equivalent to matching vertices of a plurality of vertices
saturated, and therefore the property of inheritance is performed. Thirdly, as the algorithm is correct Kuhn, if the current
matching is not possible, then there always exists a vertex, which can be satiate, without removing from the set of saturated
vertices other peaks.
Thus we have shown that our task is a weighted matroid with respect to the set of saturated
vertices of the first part, but because it is applicable to the greedy algorithm.
It remains to show that Kuhn this algorithm is a greedy algorithm.
However, it is quite obvious. Kuhn algorithm at each step of trying to saturate the current node - or just spending
edge in the top of the second beat of unsaturated or finding a lengthening chain and matching alternating along it.
And in fact, in any other case already saturated vertices not cease to be unsaturated and unsaturated vertices in
the previous steps of the first part and not saturated at this step. Thus, the algorithm is greedy Kuhn algorithm for
constructing an optimal subset of the matroid independent, which completes our proof.
Edmonds algorithm for finding the
greatest matchings in arbitrary
graphs
Dan unweighted undirected graph with vertices. You want to find in it a maximum matching, etc.
this is the largest (in power), the set its edges, that no two edges of the selected not incident
each other (ie, have no common vertices).
Unlike the case of a bipartite graph (see Kuhn algorithm), In the column may be present cycles of
odd length, which greatly complicates the search for ways of increasing.
We first give a theorem of Berge, from which it follows that, as in the case of bipartite graphs,
maximum matching can be found using the increasing ways.
Berge's theorem (Claude Berge, 1957 g). Matching is the largest, and then only if it does not exist
for increasing the chain.
Proof of necessity. Suppose that for matchings there is an increasing chain
. We show how to go to the matchings more power. Perform alternating matchings along
And remove
this circuit, i.e. include in the matching edges , , ..., from
matching edges , , ..., . The result is likely to be obtained
correct matching, whose power will be one higher than the matching (Since we added ribs
and removed edge).
The proof of sufficiency. Suppose that for matchings there magnifying
chain, prove that it is the greatest. Let - A maximum matching. Consider
symmetric difference (Ie, the set of edges belonging to either Or But not
an
both simultaneously). We show that contains the same number of ribs d (Because we excluded
Only their common edge, then it will follow and ). Note that consists of
simple paths and cycles (since otherwise one would be the top two edges incident immediately any matchings
which is impossible). Further, the cycles may not have an odd length (for the same reason). Chain neither
may have an odd length (otherwise she was for increasing the chain That contradicts or
for That is contrary to its maximum). Finally, circuits, and even cycles even length ribs
an an
alternately included d , Which means that occurs the same number of edges from d . As
mentioned above, it follows that Ie is a maximum matching.
Berge's theorem provides the basis for the algorithm Edmonds - find increasing chains and striping
along them until the chains are increasing.
Nevertheless, it is possible to construct a graph in which at a certain order in the adjacency lists algorithm Kuhn deadlocked.
first 4, and then to 1, and when you start it from the top three from the top two will go first to one, and only then to 6).
As we have seen in this example, the problem is that when released into the cycle of odd length bypassing can go on a
cycle in the wrong direction. In fact, we are only interested in "saturated" cycles, ie in which there is saturated edges, where
the cycle length is. In this cycle, there is exactly one vertex not saturated edges of this cycle, we call it base (Base). To the
top of the base is suitable alternate path
even (possibly zero) length, starting at the free (ie not owned matchings) top, and this way is called
stem (Stem). Finally, the subgraph formed by the "saturated" odd cycle, called flower (Blossom).
Idea of the algorithm Edmonds (Jack Edmonds, 1965) - in compression flowers (Blossom shrinking). Compression of the
flower - the contraction of all the odd cycle in a pseudo-vertex (respectively, all the edges incident to the vertices of the
cycle are incident pseudo-top). Edmonds algorithm searches the graph all the flowers, compresses them, and then the graph is
not "bad" cycles of odd length, and such a graph (called "surface" (surface)
graph) can already look magnifying simple bypass circuit in depth / width. After finding
increasing the chain in the surface graph must "develop" the flowers, thereby restoring the
magnifying circuit in the original graph.
However obvious that compression can not be broken flower graph structure, namely that if the space existed chain increases, it
also exists in the graph Obtained after the compression of a flower, and vice versa.
void edmonds() {
for (int i =0; i <n; + + I)
if (top i not matchings) {
int last_v = find_augment_path (i);
if (last_v! = -1)
perform striping along the path from i to last_v;
}
}
Effective implementation of
Immediately evaluate the asymptotic behavior. Altogether there iterations, each of which crawls in width for
Moreover, the compression operation can be flowers - they can be . Thus, if
we learn how to squeeze a flower , The total amount to the asymptotic behavior of
behind the algorithm .
The main difficulty of compression operations are flowers. If you execute them directly combining
adjacency lists and removing one of the extra vertices of the graph, then the asymptotic behavior of compression
will be a single flower ,
In addition, difficulties arise when "unfolding" of flowers.
Instead, we shall, for each vertex of the graph to maintain a pointer to the base of the flower, to which it belongs (or yourself, if
node does not belong to any of the flower). We need to solve two problems: the compression
flower for when it is detected, as well as convenient storage of all information for later
increasing the alternation along the way.
So, one iteration of the algorithm is a circumvention Edmonds width executed from a given
free vertices . Will gradually build a tree traversal in width, and the path to it any
vertices will be alternating path starting with a free vertex . For
ease of programming will be put in place only those peaks, the distance to which the tree is even ways (we call such vertices even
- that is the root of the tree, and the second ends of edges in matchings). The tree itself will be stored in an array of ancestors, in
which for each odd vertex (ie before
which the distance in the tree odd ways, ie it first ends of edges in matchings) will store ancestor - even vertices. Thus, the recovery
path of the tree, we need to use the arrays alternately
and Wherein - For each vertex adjacent to it contains a matching problem, or If one is not.
Now it becomes clear how to detect cycles of odd length. If we are out of the current top while crawling across arrive at a
vertex , Is the root or belonging matchings and tree paths (ie which is not equal to -1), we found a flower. Indeed, under
these conditions
and the top , And the top are even vertices. Distance from them to their lowest common ancestor has
one parity, so we found a cycle of odd length.
Learn to compress cycle. So, we found an odd cycle when considering edges where and -
even vertices. Find their least common ancestor, and he will be the base of the flower. Easy to see that the base is too
even vertex (as odd vertices in the tree paths have only one son). However, it should be noted that - it may
pseudovertex, so we actually find the base of the flower,
is the lowest common ancestor of vertices and . Implement immediately to find the lowest common
ancestor (we are quite satisfied asymptotics )
int v, u; / / edge (V, u), when considering which was discovered flower
int b = lca (v, u);
memset (blossom, 0, sizeof blossom);
mark_path (v, b, u);
mark_path (u, b, v);
where the passes on its way from the top to the base of the flower, shall enter into the
function special
array them and affix ancestors for even vertices. Parameter - For son
the summit (with this option we we close the cycle in the ancestors).
Finally, we realize the basic function - That will look for the path increases
from the free vertex and return to the last vertex of the path, or If increasing path
not found.
Initially, we perform initialization:
Next is a wide detour. Considering the next edge We have a few options:
● Edge nonexistent. By this we mean that and belong to a single compressed pseudo-
vertex ( ), So in the current column of the edge surface is not. Also in this case,
There is another case when an edge already belongs to the current matchings; because we assume that the vertex
is an even vertex, then pass along this edge is in the tree paths climb to the top of ancestor
That is unacceptable.
● Edge closes a cycle of odd length, ie detected flower. As mentioned above, an odd cycle length
is detected under the conditions:
In this case it is necessary to compress the flower. It has already been examined in detail this
process, we present here its implementation:
● Otherwise - it is "normal" edge, we proceed as in a normal search in width. The only subtlety - while making sure
the top of this we have not yet visited, we must look not to the array, and the array - He filled for visited odd
vertices. If we are in the top still did not come, and it was unsaturated, we found a magnifying chain terminating
at the top , Return it.
if (p[to] == -1) {
p[to] = V;
if (match[to] == -1)
return to;
to = match[to];
used[to] = true;
q[qt + +] = To;
}
Finally, we determine all the global arrays, and implementation of the main program for finding
the greatest matchings:
const int MAXN = ...; / / maximum possible number of vertices in the input
graph
int n;
vector <int> G[MAXN];
int match[MAXN], P[MAXN], Base[MAXN], Q[MAXN];
bool used[MAXN], Blossom[MAXN];
...
int main() {
Graph reading ... ...
This optimization significantly (several times) will speed up the algorithm on random graphs.
Further optimization
In all the above operations with flowers thinly veiled operations with disjoint sets that can be performed much more efficiently (see
System of disjoint sets). If we rewrite the algorithm
using that structure, then the asymptotic behavior of the algorithm decreases to . Thus, for
arbitrary graphs, we got the same asymptotic estimate that in the case of bipartite graphs (Kuhn
algorithm), but significantly more complex algorithm.
Covering ways directed acyclic graph
Given a directed acyclic graph. Cover it requires the least number of ways, ie to find the smallest power
over the tops of a set of disjoint simple paths, such that each vertex belongs to a path.
Definition
Let a graph with vertices ( - Even).
Then Tutte matrix (Tutte) is the following matrix :
where () - is either independent variable corresponding to the edge between vertices and either
identically zero, if the edges between vertices not.
Thus, in case of a complete graph with vertices Tutte matrix contains
independent variables, if any edges in the graph are missing, then the corresponding elements of the matrix are converted to
zeros Tutte. Generally, the number of variables in the matrix Tutte coincides with the number of edges.
Tutte's theorem
Consider the determinant Tutte matrix. It is, generally speaking, a polynomial in the variables .
Tutte's theorem reads as follows: in the graph there is a perfect matching if and only if when
polynomial is not identically zero (ie has at least one term with a nonzero
ratio). Recall that the matching is called perfect if it saturates all the vertices, ie its power is .
Canadian mathematician William Thomas Tutte (William Thomas Tutte) first pointed out the close relationship between
matchings in graphs and determinants of matrices (1947). A simpler form of this connection later discovered Edmonds
(Edmonds) in the case of bipartite graphs (1967). Randomized algorithms for finding maximum matching value themselves and
edges of this matching were proposed later, respectively,
Lovas (Lovasz) (1979), and Rabin (Rabin) and Vazirani (Vazirani) (in 1984).
To assess Probability of Error You can use Lemma Schwarz-Sippel (Schwartz-Zippel), which
states that the probability of the vanishing of a nonzero polynomial of degree-by substituting the
values of variables as random numbers, each of which can take options values - this probability
satisfies:
For example, when using standard random number function C + + we find that this probability
at is about one percent.
Asymptotics solution turns out to be (Using, for example, Gauss) Multiplied
the number of iterations of the test. It is worth noting that the asymptotic solution is far behind the decision Edmonds
compression algorithm flowersHowever, in some cases, more preferably from
ease of implementation.
Reestablish itself as a perfect matching set of ribs is more challenging. The easiest, albeit slow,
recovery will be one of this matching one edge: iterate through the first edge response, choose it
so that there remains a graph perfect matching, etc.
Proof of Tutte
To understand well the proof of this theorem, we first consider a simple result - Edmonds
obtained for the case of bipartite graphs.
Edmonds theorem
Consider a bipartite graph in which for each lobe vertices. Form the matrix In which, by analogy with the matrix
Thatta is a separate independent variable, if an edge is present in the graph,
and identically zero otherwise.
This matrix is similar to the matrix of Thatta, but Edmonds matrix has half the difference, and each
edge here has only one cell of the matrix.
We prove the following Theorem: Determinant is nonzero if and only if there is a bipartite
graph, a perfect matching.
Proof. We expand the determinant according to its definition, as the sum over all permutations:
Note that since all non-zero elements of the matrix - Various independent variables, in this sum all the nonzero terms are
different, but because no reductions in the summation occurs. It remains to note that any non-zero term in this sum is disjoint set on
the tops of the ribs, ie a perfect matching. Conversely, any perfect matching corresponds to a non-zero term in this sum. Coupled
with the above, this proves the theorem.
notice that the antisymmetric matrix satisfies And now we get the chain of equalities:
which implies that for odd determinant must be zero.
Secondly, it turns out, that in the case of antisymmetric matrices of even size of their determinant is always
can be written as the square of a polynomial in the variables of the matrix elements (called the Pfaffian
polynomial (pfaffian), and the result is due to Muir (Muir)):
Thus, each term in the Pfaffian - a work of such elements of their indices
collectively constitute a partition of at pairs. Before each term has
their ratio, but his view we are not interested.
Proof of Tutte
Using the second and third property from the previous paragraph, we see that the determinant of Thatta is the square of the
sum of terms of the kind that each term
- The product of the matrix elements whose indices are not repeated and cover all the numbers from to
. So again, as in the proof of Theorem Edmonds, every non-zero term in this sum corresponds to perfect
matching in a graph, and vice versa.
Formulation
Rank Tutte matrix coincides with the double value maximum matching in this graph.
Application
To apply this theorem in practice, you can use the same technique of randomization, that in the above algorithm for the
Tutte matrix, namely substitute random values of variables, and to find the rank of the resulting numerical matrix. Rank of
the matrix, again, searched for using
modified Gauss, see here.
However, it should be noted that the algorithm given in the previous lemma Schwarz-Sippel inapplicable explicitly and
intuitively it seems that the error probability becomes higher here. However, allegedly (see work Lovasz (Lovasz)), and that
here the probability of error (ie, that the rank of the resulting matrix will be less than twice the size of the maximum
matching) does not exceed (where As above, denotes
size of the set from which the random number).
Proof
The proof will follow from one propertiesKnown from linear algebra. Suppose we are given
antisymmetric matrix And let set and - any two subsets
set And the dimensions of these sets are identical. We denote matrix derived from
Only rows with indices of and columns with indices . Then we have:
We show how this property allows you to set conformity between the rank of the matrix
Thatta and the magnitude of the maximum matching.
On the one hand, consider the graph in a maximal matching, and denote the set of vertices saturable them
through. Then the determinant is nonzero (Tutte's theorem).
Investigator, the rank of Thatta at least not less than twice the size of the maximum matching.
Conversely, let the rank of equal . This means that we found such a submatrix ,
where Whose determinant is nonzero. But according to the above property, this means that
one of the
matrices , has nonzero determinant that Tutte's theorem means that
subgraph induced by the set of vertices or, there is a perfect matching (and the value of
equal ). Consequently, the rank of the matrix can not be greater than the maximum matchings that
completes the proof.
Application
This theorem can be used to restore the edges themselves maximal matching. First have to select the
subgraph, which contains the desired maximal matching (this can be done in parallel with the rank of the
search algorithm).
After that the problem reduces to finding a perfect matching on this numerical matrix obtained from the matrix Tutte. Here we
apply Theorem Rabin-Vazirani - find the inverse matrix (which can be
make the modified algorithm for Gaussian ), We find in it any nonzero element is removed from
graph, and repeat the process. Asymptotics of such solutions is not the fastest - , But instead
simplicity obtain solutions (compared, for example, Edmonds compression algorithm flowers).
Proof of Theorem
Recall the well-known formula for the elements of the inverse matrix :
where indicated by the cofactor, ie this number is multiplied by the determinant of a matrix
obtained from Disposal -Th row and -Th column.
Hence we immediately obtain that the element differs from zero if and only if the matrix strike-
-Th row and-th column has a nonzero determinant that by applying Tutte's theorem, means a high probability that the
Literature
● William Thomas Tutte. The Factorization of Linear Graphs [1946]
● Laszlo Lovasz. On Determinants, Matchings and Random Algorithms [1979]
Definition
Suppose we are given an undirected
graph with and peaks ribs.
Branch Connectivity column is the smallest number of edges that must be removed to
ceased to be a connected graph.
For example, for a disconnected graph edge connectivity is equal to zero. For a connected graph with a single bridge
edge connectivity is equal to unity.
Say that the set of edges shares vertices and, if you remove these edges from the graph vertices and
are in different connected components.
Clearly, an edge connectivity of the graph is the minimum of the smallest number of edges that
separate the two peaks and , Taken among all possible pairs .
Properties
Whitney ratio
Value for Whitney (Whitney) (1932 g) between Branch Connectivity ,vertex connectivity and
the smallest vertex degree of :
Thus, .
To prove the second inequality. Consider a vertex of minimal degree, then we can remove all
allied ribs and thereby separate it from the rest of the top graph. Consequently, .
Interestingly, the Whitney inequality can not be improved: Ie for all triples of numbers satisfying this inequality,
there exists at least one corresponding graph. See Problem "The construction of a graph with
these quantities vertex and edge connectivity and the lowest vertex degree ".
Ford-Fulkerson theorem
Ford-Fulkerson theorem (1956 g)
For any two vertices of the largest number of edge-disjoint circuits connecting them, equal
to the minimum number of edges separating these peaks.
algorithm is as follows:
Asymptotic behavior of the algorithm using \ edmonds_karp {Edmonds-Karp algorithm for finding the
maximum flow} is obtained, however, it should be noted that hidden in the asymptotic
constant is very small, since it is practically impossible to create a graph algorithm to find the maximum flow
slowly worked simultaneously for all the source and drain.
A special algorithm
Using streaming terminology, this problem - it is the task of finding global
minimal cut.
To solve it developed special algorithms. At this site contains one of which - algorithm Curtains-
Wagner, running time or .
Literature
● Hassler Whitney. Congruent Graphs and the Connectivity of Graphs [1932]
● Frank Harari. Graph Theory [2003]
Edge connectivity. Properties and being
Definition
Suppose we are given an undirected graph with and peaks ribs.
Vertex connectivity graph is the smallest number of vertices, you want to delete, to graph
ceased to be connected.
For example, for a disconnected graph vertex connectivity is zero. For a connected graph with a single point of articulation
vertex connectivity equal to one. For a complete graph vertex connectivity considered equal (As which pair of
vertices we may choose, even the removal of all remaining vertices will not make them disconnected). For all graphs, but
complete, vertex connectivity does not exceed - Because you can find a pair of vertices between which there is
no edge, and remove all other top.
Say that the set of vertices shares vertices and, if the removal of these vertices of the graph vertices
and are in different connected components.
It is clear that the vertex connectivity of the graph is equal to the minimum of the smallest number of
vertices separating two vertices and , Taken among all possible pairs .
Properties
Whitney ratio
Value for Whitney (Whitney) (1932 g) between Branch Connectivity , The vertex connectivity
and the smallest vertex degree of :
Thus, .
To prove the second inequality. Consider a vertex of minimal degree, then we can remove all
allied ribs and thereby separate it from the rest of the top graph. Consequently, .
Interestingly, the Whitney inequality can not be improved: Ie for all triples of numbers satisfying this inequality,
there exists at least one corresponding graph. See Problem "The construction of a graph with
these quantities vertex and edge connectivity and the lowest vertex degree ".
For this .
and .
. and .
Graphing with the stated values of the vertex and
edge connectivity and the lowest degree of the
vertices
,
Whitney ratio
Value for Whitney (Whitney) (1932 g) between Branch Connectivity ,vertex connectivity and
the smallest vertex degree of :
Thus, .
To prove the second inequality. Consider a vertex of minimal degree, then we can remove all
allied ribs and thereby separate it from the rest of the top graph. Consequently, .
Interestingly, the Whitney inequality can not be improved: Ie for all triples of numbers satisfying this
inequality, there exists at least one corresponding graph. This we prove constructive, showing how
to construct the corresponding graphs.
Decision
Verify whether the data number , and Whitney ratio. If not, then there is no answer.
Otherwise, we construct the graph itself. It will consist of vertices, wherein the first
vertices form polnosvyaznom subgraph and second vertices also form polnosvyaznom subgraph. Except
that connect the two parts ribs so that these ribs of the first portion are adjacent the tops, and the other part - vertices.
Easy to see that the resulting graph will have the necessary characteristics.
Inverse problem SSSP (inverse-SSSP -
inverse problem of the shortest paths
from one vertex)
There is a weighted undirected multigraph G of N vertices and M edges. An array P [1 .. N] and contains
some initial vertex S. I want to change the edge weights so that all IP [I] was equal to the length of the
shortest path from S to I, and the sum of all changes (sum of absolute changes of edge weights) would lower.
If this is not possible, then the algorithm must give "No solution". Do negative edge weight is prohibited.
Thus, just sorting through all the edges and having considered the situation for each edge (O
(1)), we solve the inverse problem in linear time SSSP.
If at some point we are trying to change has altered edge, then obviously you can not do, and should issue a "No solution".
Furthermore, some peaks can not be achieved and the required estimate of the shortest path, then the answer will be too "No
solution". In all other cases (except, of course, is clearly incorrect values in the array P, ie, P [S]! = 0 or negative values), the
answer will be.
Implementation
The program displays "No solution", if there is no solution, otherwise displays the first line of the
minimum amount of changes weights of the edges, and in the next M lines - new edge weights.
bool ok = true;
vector <int> cost (m), cost_ch (m), decrease (n, INF), decrease_id (n, -
1); decrease [0] = 0;
for (int i = 0; i <m; + + i) {
int a, b, c; / / Current edge (a, b) the
price of c cost [i] = c;
for (int j = 0; j <= 1; + + j) {
int diff = p [b] - p [a] -
c; if (diff> 0) {
ok & = cost_ch [i] == 0 | | cost_ch [i] ==
diff; cost_ch [i] = diff;
decrease [b] = 0;
}
else
if (-diff <= c &&-diff <decrease [b])
{decrease [b] =-diff;
decrease_id [b] = i;
}
swap (a, b);
}
}
if (! ok)
cout << "No solution";
else {
long long sum = 0;
for (int i = 0; i <m; + + i) sum + = abs (cost_ch
[i]); cout << sum << '\ n';
for (int i = 0; i <m; + + i)
printf ("% d", cost [i] + cost_ch [i]);
}
The inverse problem of MST (inverse-MST -
inverse problem of the minimum spanning tree) in O (N
M2)
Given a weighted undirected graph G with N vertices and M edges (without loops and multiple edges). It is known
that the graph is connected. Also listed some skeleton T of this graph (ie, selected N-1 edges that form a tree with
N vertices). Want to change the weights of the edges so that the specified frame T is a minimal skeleton of the
graph (more precisely, one of the minimum spanning tree), and make it so that the total change of all was the
smallest scales.
Decision
We reduce task inverse-MST to the problem min-cost-flow, more precisely, to the problem of dual min-
cost-flow
(In the sense of the duality of linear programming problems); then solve the latter problem.
So, let G be a graph with N vertices, M edges. Weight of each edge is denoted by Ci. We assume
without loss of generality that the edges with numbers from 1 to N-1 are edges T.
It can be noted that as in our problem T backbone belong ribs 1 .. N-1, we can write this condition
as follows:
2. Paths Count
The notion of graph paths directly related to the previous theorem. Let there
3. Mathematical formulation of
Formally task inverse-MST written in this way:
Finally, just change "minimize" to "maximize", and in the amount of change all signs to the contrary:
В any time of the algorithm potentials suchThat the following conditions are satisfied:
if Fij = 0, then CPIij> = 0
if Fij = Uij, then CPIij <= 0
otherwise CPIij = 0
The algorithm starts with zero flow, and we need to find some initial potential values that satisfy the above conditions. It is
easily verified that this method is one of the possible solutions:
PIt = 0
Actually, the algorithm min-cost-flow consists of several iterations. At each iteration, we find
the shortest path from s to t in the residual network, and as the weights of the edges using the
residual value of CPI. Then we increase the flow along the path found by one and update the
potentials as follows:
PIi - = Di
where Di - found the shortest distance from s to i (again, in the residual network with edge weights CPI).
Sooner or later, we will find the path from s to t, which consists of a single edge (s, t). Then after this iteration, we
should complete the algorithm: indeed, if we do not stop the algorithm, it will already be on the way to a non-
negative value, and add them to the answer is not necessary.
By the end of the algorithm we obtain a solution of the assignment (in the form of flow Fij) and the solution
of the dual assignment problem (in the array PIi).
(With PIi will have to conduct a slight modification: the total values PIi take PIs, because its values are
meaningful only when PIs = 0)
6. Subtotal
So, we decided to dual task assignment, and therefore the task of inverse-MST. We
estimate the asymptotics the resulting algorithm.
First we'll need to construct a graph paths. To do this just for each edge j T preorder traversal to find a way
skeleton T P [j]. Then we construct a graph paths for O (M) * O (N) = O (NM).
Then we find the initial values of the potentials for the O (N) * O (M) = O (NM).
Then we iterate min-cost-flow, all iterations is no more than N (as N goes from the source of the ribs, each with a bandwidth = 1),
at each iteration we are looking for ways to graph the shortest path from source to all other vertices. Since the vertices in the
graph paths equals M +2, and the number of edges - O (NM), then
if you implement search shortest paths simplest version of Dijkstra's algorithm, each iteration of the min-cost-flow
will perform in O (M2), and the whole algorithm min-cost-flow finishes in O (N M2).
The resulting asymptotic behavior of the algorithm is O (N M2).
Implementation
We sell all the above described algorithm. The only change - instead Dijkstra's algorithm applicable Leviticus
algorithm, which in many tests should run slightly faster.
struct rib {
int v, c, id;
};
struct rib2 {
int a, b, c;
};
int main () {
int n, m;
cin >> n >> m;
vector <vector <rib>> g (n); / / Graph format in adjacency lists
vector <rib2> ribs (m); / / All the ribs in a single list
Graph reading ... ...
for (; ;) {
vector <int> id (nn);
deque <int> q;
q.push_back (s); vector
<int> d (nn, INF); d [s]
= 0;
vector <int> p (nn, -
1); while (! q.empty
()) {
int v = q.front (); q.pop_front
(); id [v] = 2;
for (int i = 0; i <nn; + + i)
if (f [v] [i] <u [v] [i]) {
int new_d = d [v] + c [v] [i] - pi [v] +
pi [i];
if (new_d <d [i]) {d
[i] = new_d; if
(id [i] == 0)
q.push_back (i);
else if (id [i] == 2)
q.push_front (i);
id [i] = 1;
p [i] = v;
}
}
}
for (int i = 0; i <nn; +
+ i) pi [i] - =
d [i];
for (int v = t; v! = s; v = p
[v]) {int pv = p [v]; +
+ F [pv] [v], - f [v]
[pv];
}
if (p [t] == s) break;
}
}
Paint edges of the tree
This is a fairly frequent problem. Given tree G. request comes in two forms: the first kind - paint some edge, a second
look - request amount colored edges between two vertices.
Here will be described fairly simple solution (using tree segments) That will respond to requests for O (log N),
with preprocessing (pretreatment of wood) for O (M).
Decision
First, we have to implement LCAThat each request is the second type (i, j) is reduced to two requests (a,
b), where a - ancestor b.
We now describe preprocessing actually our problem. Run dfs root of the tree, this dfs make some list visit vertices
(each vertex is added to the list when it comes to search, and each time after dfs son returns from the current vertex) -
incidentally, This list is used by the algorithm LCA. This list will be present each edge (in the sense that if i and j - the
ends of the ribs, the list always find a place where i and j are contiguous to each
other), and contain exactly two times: in the forward direction (from i to j, where i vertex closer to the top
than the vertex j) and reverse (from j to i).
Construct two tree lengths (For amounts with a single modification) this list: T1 and T2. Tree T1 will consider each
edge in the forward direction, and the tree T2 - on the contrary, only in reverse.
Let entered the regular inquiry species (i, j), where i - ancestor j, and requires to determine how much colored
edges in the path between i and j. Find i and j in the list traversal depth (we definitely need a position where they
first met), let it be some positions p and q (this can be done in O (1), if we calculate these positions in advance
during preprocessing). Then the answer is the sum of T1 [p .. q-1] - amount T2 [p .. q-1].
Why? Consider the segment [p; q] list traversal in depth. It comprises edges us desired path from i to j, but also contains a
plurality of ribs which lie on the paths of the other i. However, between the right ribs us
and the rest of the ribs there is one big difference: the desired edges are contained in the list only once, and in the
forward direction, and all other edges will meet twice, both literally and
reverse direction. Consequently, the difference T1 [p .. q-1] - T2 [p .. q-1] will give us the answer (minus one needed,
because otherwise we will take another extra edge from vertex j somewhere up or down). Request the amount of tree
segments runs in O (log N).
The answer to inquiry type 1 (about painting a rib) is even easier - we just need to update the T1 and T2,
namely to perform a single modification of the element that corresponds to our edge (the edge to find the
list of crawl, again, can be O (1) if you perform this search in preprocessing). Individual modification in the
tree segments runs in O (log N).
Implementation
Here will be given full implementation solutions, including LCA:
void sum_tree_update (vector <int> & tree, int i, int l, int r, int j, int
delta)
{
tree [i] + =
delta; if (l <r)
{
int m = (l + r) >> 1;
if (j <= m)
sum_tree_update (tree, i + i, l, m, j, delta);
else
sum_tree_update (tree, i + i +1, m +1, r, j, delta);
}
}
int sum_tree_query (const vector <int> & tree, int i, int tl, int tr, int
l, int r)
{
if (l> r | | tl> tr) return 0;
if (tl == l && tr == r)
return tree
[i]; int m = (tl + tr)
>> 1; if (r <= m)
return sum_tree_query (tree, i + i, tl, m, l,
r); if (l> m)
return sum_tree_query (tree, i + i +1, m +1, tr, l,
r); return sum_tree_query (tree, i + i, tl, m, l, m)
+ Sum_tree_query (tree, i + i +1, m +1, tr, m +1, r);
}
int main ()
{
/ / Read the
graph int n;
scanf ("% d", & n);
graph g (n), rib_ids (n);
for (int i = 0; i <n-1; +
+ i)
{
int v1, v2;
scanf ("% d% d", & v1, &
v2); - V1, - v2; g [v1].
push_back (v2); g [v2].
push_back (v1); rib_ids
[v1]. push_back (i);
rib_ids [v2]. push_back
(i);
}
}
Task 2-SAT
Task 2-SAT (2-satisfiability) - it is the task of distribution of values Boolean variables so that they
satisfy all constraints.
2-SAT problem can be represented as a conjunctive normal form, where each expression in brackets is exactly two
variable; this form is called a 2-CNF (2-conjunctive normal form). For example:
Apps
Algorithm for solving a 2-SAT can be applied in all applications where there is a set of variables, each of which
can take two possible values, and there is a connection between these values:
● Location text labels on the map or chart.
Means finding such an arrangement of marks for which no two disjoint.
It should be noted that in the general case where each label can occupy many different positions, we obtain
the problem of general satisfiability, which is NP-complete. However, if we restrict ourselves to only two
possible positions, the resulting problem is the task 2-SAT.
● Location edges when drawing graph.
Similarly to the previous paragraph, if we restrict ourselves to only two possible ways to hold an
edge, then we will come to 2-SAT.
● Scheduling Games.
This refers to a system where each team must play with each other once, and want to distribute the game to the
type of home-visiting, with some constraints.
● etc.
Algorithm
We first present the problem to another form - the so-called implicative form. Note that the expression of the form a | | b is
equivalent! A => b or! B => a. This can be interpreted as follows: if there is an expression a | | b, and
we need to make it appeal to true, if a = false, you must b = true, and vice versa, if b = false, it is
necessary a = true.
We now construct the so-called Count implications: For each variable in the graph will be two vertices,
which we denote by xi and! Xi. Edges in the graph correspond to implicative relations.
(A | | b) && (b | |! C)
! A =>
b! B =>
a! B =>!
C c => b
Pay attention to the implications of such a property of the graph, that if there is an edge a => b, then there is an edge! B =>! A.
Now, note that if for some variable x is performed, that of x is achievable! X, and from! X achievable x, then the problem has
no solution. Indeed, whatever the value for the variable x, we would have chosen, we always arrive at a contradiction - that
must be chosen and its inverse value. It turns out that this condition is not only sufficient but also necessary (proof of this fact
is the algorithm described below). Reformulate this criterion in terms of graph theory. Recall that if one of
other peaks attainable, and that the vertex is reachable from the first, these two peaks are in the
same strongly connected component. Then we can formulate criterion for the existence of
solutions follows:
To this problem 2-SAT has a solutionIt is necessary and sufficient that for any variable x and vertex x! X were in
different components of the strong connectivity Count implications.
This criterion can be checked in time O (N + M) using search algorithm for strongly connected component.
Now build your own algorithm finding a solution to the problem 2-SAT under the assumption that a solution exists. Note that,
despite the fact that a solution exists, some of the variables can be performed in that
x achievable! x, or (but not both) of! x achievable x. In this case, selecting one of the values of the variable x will lead to a
contradiction, while another choice - will not. Learn to choose between two values that which does not lead to inconsistencies.
Immediately, we note that by selecting a value, we have to run from him crawling in depth / width and mark all the values that follow
from it, ie achievable
implications in a graph. Accordingly, for vertices are labeled no choice between x and! X do not need to
their value has already been chosen and recorded. Described hereafter rule applies only to untagged still tops.
Allegedly following. Let comp [v] denotes the number of strongly connected components, which belongs to the vertex v, and the
rooms are arranged in order of topological sort of strongly connected components in the graph components (ie earlier in order
topological sorting correspond to large
numbers if there is a path from v to w, then the comp [v] <= comp [w]). Then, if the comp [x] <comp [! X], then select the value! X,
otherwise, ie
, if comp [x]> comp [! x], then we choose x.
We prove, that with this choice of values, we do not arrive at a contradiction. Let For definiteness, selected
vertex x (the case where the peak! x, proved symmetrically).
First, we prove that x is not achievable! X. Indeed, since the number of strongly connected components comp [x] more room
components comp [! X], it means that the connected component containing x, is located to the left of the connected components
containing! X, and from the first can not be achieved last .
Secondly, we prove that no vertex y, accessible from x, is not "bad", ie incorrectly, that of y achievable! y. We prove this by
contradiction. Let x be achieved from y, and y is achievable from! Y. Since x reachable from y, then, by implication graph
property from! Y is achievable! X. But by assumption of y achievable! Y. Then we see that from
achievable x! x, which contradicts the assumption that we wanted to prove.
Thus we have constructed an algorithm that finds the desired values of the variables under the
assumption that for any variable x and vertex x! X are in different strongly connected components. Above
showed the correctness of this algorithm. Consequently, we have simultaneously proved the existence of
the above criteria solutions.
Now we can collect the whole algorithm together:
● Construct a graph of implications.
● Find in this graph strongly connected components during the O (N + M), let comp [v] - is the
number of strongly connected components, which belongs to the vertex v.
● Verify that for each variable x, and the vertex x! X lie in different components, ie comp [x] ≠ comp [!
x]. If this condition is not satisfied, then return "solution does not exist."
● If comp [x]> comp [! X], then select the variable x is true, otherwise - false.
Implementation
Below is the implementation of the solution of the problem for 2-SAT is the implications of this graph g and
its inverse graph gt (ie, in which the direction of each edge reversed).
The program displays the numbers of the selected vertices or phrase "NO SOLUTION", if there is no solution.
int n;
vector <vector <int>> g, gt;
vector <bool> used; vector
<int> order, comp;
void dfs1 (int v) {used
[v] = true;
for (size_t i = 0; i <g [v]. size ();
+ + i) {int to = g [v] [i];
if (! used [to])
dfs1 (to);
}
order.push_back (v);
}
int main () {
Reading ... n, the graph g, construction graph gt ...
}
Heavy-light decomposition
Heavy-light decomposition - it is quite a common technique which can effectively solve many
problems reduce to requests on the tree.
The simplest example problems of this kind - is the next task. Given a tree, each node is assigned a number.
Receives requests form, where and - the numbers of vertices of the tree, and requires
determine the maximum number of nodes in the path between and .
way out in "Multiple queries like" find something on the interval On the way. "
All other edges are said Light. Obviously, one of the vertices could come down heavy at most one edge (because
otherwise at the top it would be the size of two sons that given
Thus, we could not pass the lighter edges. However, to move from one track to another, we
can only through the lung edge (because each way but ending at the root, contains a slight edge at the
end, and get right in the middle of the way, we can not).
Therefore, the path from the root to any vertex we can not change over routes that
QED.
We should also pay attention to the task sum of the numbers on the wayAs an example of a
problem that can be solved by a simple technique.
, And therefore the total amount of trees will also be linear segments.
Now, in order to respond to the request find the lowest common ancestor these
an
vertices (e.g., binary method of lifting). Now the problem is reduced to two requests: d , Each
of which we can answer this way: we find ways in which lies lower vertex, make a request to this path, go to
the top-end of the path, again define the way in which we were and make a request to him, and so on, until
we reach the path comprising .
Gently should be the case when, for example, and were in the same way - then you get the maximum
to this path should not do on the suffix, and the domestic subsegments.
Thus, in the process of answering one subquery we pass along paths, each of them making a request of the maximum on the
suffix or prefix / subsegments (request for prefix / subsegments could be a one time only).
If additionally predposchitat highs in each path at all suffixes, you get a solution for - Because Request no
maximum on the suffix happens only once, when we get to the top .
Although this problem can be solved with the help of heavy-light decomposition, building on each path
segment tree for the sum (or simply predposchitav partial sums, if the problem no change requests),
this problem can be solved simpler techniques.
If no modification requests, the amount to learn the path between two vertices can be in parallel with the search for the two
vertices in the LCA binary algorithm lifting - It is enough during preprocessing for
LCA counting not only 's Ancestors each vertex, but also the sum of the path to that ancestor.
There is also a fundamentally different approach to this problem - consider Euler traversal of the tree, and build a segment
tree above him. This algorithm is discussed in article similar to the decision problem. (And if requests
no modification - it is enough to do predposchetom partial sums, without a tree segments.) Both of these methods provide
Each request repainting the way turn into two subqueries and where - The lowest common ancestor of
vertices and (Found, for example, binary algorithm lifting), And each of those subrequests - in
queries to the trees above the track segments.
Description
Put all the coordinates of the ends of segments in the array X and sort it by value coordinates.
Additional condition for sorting - with equal coordinates must first go left ends. In addition, for each element of the array will be stored,
it refers to the left or to the right end of the segment. Now go through the entire array, with counter C overlapping segments. If C is
non-zero, then the result is added to the difference Xi - Xi-1. If the current element refers to the left end, then increment the counter C,
otherwise reduce it.
Implementation
unsigned result = 0;
unsigned c = 0;
for (unsigned i = 0; i <n * 2; i + +)
{
if (c && i)
result + = unsigned (x [i]. first - x [i-1].
first); if (x [i]. second)
+ + C;
else
- C;
}
return result;
}
The sign area of a triangle and the predicate
"Clockwise"
Definition
Suppose we are given three points , , . Find the value of sign area Triangle Ie the area
of this triangle, taken with the plus or minus sign depending on the type of pivot points formed by
, , : Counterclockwise or her accordingly.
It is clear that if we learn how to calculate such a landmark ("targeted") area, and we can find the usual
area of any triangle, and also be able to check, clockwise or counterclockwise directed any triple of
points.
Calculation
Use the notion of Bias (Pseudoscalar) product of vectors. It just is twice the area of the triangle
sign:
where the angle taken oriented, i.e. is the angle of rotation between the vectors counterclockwise. (Module
skew product of two vectors is equal to the modulus Vector their work.)
Skew product is calculated as the value of the determinant of the coordinates of points:
You can group the third term with the first two, got rid of one multiplication:
This formula is convenient to record and store in a matrix form as the following determinant:
Implementation
Function that computes the area of the triangle twice landmark:
int triangle_area_2 (int x1, int y1, int x2, int y2, int x3, int y3) {
return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
}
double triangle_area (int x1, int y1, int x2, int y2, int x3, int y3) {
return abs (triangle_area_2 (x1, y1, x2, y2, x3, y3)) / 2.0;
}
Function that checks whether the specified forms triple points clockwise rotation:
bool clockwise (int x1, int y1, int x2, int y2, int x3, int y3) {
return triangle_area_2 (x1, y1, x2, y2, x3, y3) < 0;
}
Function that checks whether the specified forms triple points counterclockwise rotation:
bool counter_clockwise (int x1, int y1, int x2, int y2, int x3, int y3) {
return triangle_area_2 (x1, y1, x2, y2, x3, y3) > 0;
}
Checking on the intersection of two segments
Given two segments and (They may degenerate to a point). Need to check whether or not they intersect. If
you want to find itself further point (s) of intersection, then see relevant article.
special cases, but its main drawback - the fact that he does not find itself crossing point.
Implementation:
struct pt {
int x, y;
};
In order to optimize the test of bounding box is moved to the beginning, to calculate areas - as it
is more "light" verification.
Needless to say, this code applies to the case of real coordinates, just all comparisons with zero
should be made by epsilon (and avoid multiplying two real-values ,
multiplying instead of signs).
struct pt {
int x, y;
};
Here first factor is calculated - The denominator in Cramer. If , The coefficients and directly proportional,
and the lines are parallel or coincide. In this case, you should check whether or not they are the same, which is necessary to
check that the coefficients are directly proportional to the same factor, which is sufficient to compute the determinant of the
following two if they are both zero, then the lines are the same:
If the , The lines intersect, and Cramer's rule we find the point of intersection and check
it belongs to both segments.
It should be noted that if the starting point coordinates have been real-valued, it is necessary to normalize the direct (i.e.,
bring them to a state that the sum of the squares of the coefficients and equal to one), otherwise error when compared
to the parallel lines and a match may be too large.
Finding the equation for a straight line segment
Task - given the coordinates of the end of the segment to build a line through it.
We believe that the segment is non-degenerate, ie has a length greater than zero (otherwise,
of course, passes through infinitely many different lines).
Two-dimensional case
Let a segment Ie know the coordinates of its ends , , , .
Required to construct equation of a line in the planePassing through this segment, i.e. find
the coefficients , , straight-line equation:
Note that the desired triples Passing through a given period, infinitely many:
You can multiply all three coefficients on an arbitrary non-zero number and get the same
line. Therefore, our task - to find one of these triples.
It is easy to see (by substituting these expressions and the coordinates of points and in
the equation of the line), which fits the following set of coefficients:
Integer case
An important advantage of this method of constructing direct is that if all the coordinates are integers, then the
obtained coefficients will also integer. In some cases this allows geometric operations, all without resorting to real
numbers.
However, there is a slight drawback: for the same line can be obtained different coefficients triples. To avoid this, but not away
from the integer coefficients, we can apply the following method,
Real-valued case
When working with real numbers should always be aware of the errors.
Coefficients are obtained and we have about the original coordinates, the coefficient - already about a square of them. It
may already be sufficiently large numbers, and, for example, intersection of the lines they will become more
more, which can lead to large errors even when rounding the original coordinates, the order of .
Therefore, when working with real numbers, it is desirable to produce a so-called normalization line: namely, do the
Ie straight - it all points that can be obtained from the point adding vector
with an arbitrary coefficient.
Construction line in parametric form by coordinates endpoints - trivial, we simply take one end of the
segment as a point And the vector from the first to the second end - for the vector .
Intersection point
Suppose we are given two lines defined by its coefficients and . Need to find their
point of intersection, or to find out what the lines are parallel.
Decision
If two lines are not parallel, then they intersect. To find the point of intersection of the two is enough
to make direct system of equations and solve it:
Using the formula Cramer, immediately find the solution of the system, which is the required intersection point:
the system has no solutions (direct parallel and do not coincide), or has infinitely many (direct coincide). If you
need to distinguish between these two cases, it is necessary to verify that the coefficients are directly proportional
with the same coefficient of proportionality, the coefficients and , Which is enough to calculate the
determinant of the two if they are both zero, then the lines are the same:
Implementation
struct pt {
double x, y;
};
struct line {
double a, b, c;
};
Algorithm
Segments will work with both direct: construct two segments of direct equation, checking whether parallel lines. If
the lines are not parallel, it's simple: find their intersection point and check that it belongs to both segments (it is
sufficient to verify that the point belongs to each segment in the projection on the axis and axle separately).
As a result, in this case the answer is either "empty" or single point found.
A more complicated case - if the lines were parallel (same applies here the case where one or both segments degenerated into points).
In this case, we must verify that both segments lie on a line (or, in the case when they both degenerate to the point - that this point is
the same). If it is not, then the answer is - "empty". If so, then the answer
- An intersection of two segments lying on the same line that is implemented is quite simple - it is
necessary to take the maximum of the left end and right ends of the minimum.
At the beginning of the algorithm write the so-called "check for bounding box" - first, it is necessary for
the case when the two segments are on a line, and secondly, it is a lightweight verification, allows the
algorithm to work faster on average random tests.
Implementation
We present here a complete implementation, including all auxiliary functions for working with points and lines.
The main feature here is the Which intersects two segments transmitted to it, and if they
intersect at least one point, then returns And arguments and returns the start and
end response interval (in particular, when the answer - the only point that is returned beginning and end are the same).
struct pt {
double x, y;
struct line {
double a, b, c;
line() {}
line (pt p, pt q) {
a = p.y - q.y;
b = q.x - p.x;
c = - a * p.x - b * p.y;
norm();
}
void norm() {
double z = sqrt (a * a + b *
b); if (abs(z) > EPS)
a / = z, b / = z, c / = z;
}
Method 1
This is easily done if iterate over all edges and fold area of the trapezoid bounded each edge.
Area must be taken so familiar with how it will turn out (thanks to sign all the "extra" area will be reduced).
Ie formula is:
Code:
Method 2
You can do otherwise. We choose an arbitrary point O, iterate over all the edges, adding to the response-oriented area
of the triangle formed by the edge and the point O (see Oriented
area of a triangle). Again, thanks mark, any excess space will be reduced, and will only answer.
This method is good because it is easier to generalize to more complex cases (eg, when some of
the parties - not straight and circular arc).
Pick's theorem. Finding the square
lattice polygon
Polygon without self-called lattice if all its vertices are the points with integer coordinates
(Cartesian).
Pick's theorem
Formula
Suppose we are given some lattice polygon with nonzero area.
Denote its area through; number of points with integer coordinates lying strictly inside the
polygon - through ; number of points with integer coordinates lying on the sides of the
polygon - through .
Then the relation called by Lance:
In particular, if the values of I and B for a polygon, it is possible to calculate the area per
Even without knowing the coordinates of its vertices.
This ratio is discovered and proved the Austrian mathematician Georg Alexander Pick (Georg Alexander Pick) in 1899
Proof
The proof is done in several stages: from the simplest shapes to arbitrary polygons:
● The unit square. In fact, for him , , And the formula is true.
● Arbitrary nondegenerate rectangle with sides parallel to the coordinate axes. For
proof of the formula denoted by and the lengths of the sides of the rectangle. Then we find:
, , . Direct substitution we see that the formula
True peak.
● Angled triangle with legs parallel to the axes. To prove this, we note that any such triangle can be obtained
by cutting off some of its diagonal rectangle. Denoting the number of integer points lying on the diagonal,
we can show that the formula holds for Pick such a triangle, regardless of the .
● Arbitrary triangle. Note that any such triangle can be converted into a rectangle by attaching to the sides of right
triangles with legs parallel to the axes
coordinate (this will need no more than 3 such triangles). From here you can get a correct formula for
Pick any triangle.
● Arbitrary polygon. To prove this triangulate it, ie divided into triangles with vertices at integer points. For one triangle
formula Peak we have already proved. Next, we can prove that in addition to any arbitrary polygon triangle formula Peak
retains its validity. Hence, by induction, it follows that it is true for any polygon.
Demonstrated that Reeve (Reeve), proposing in 1957 to consider the tetrahedron (now
called tetrahedron Riva) with the following vertices:
where - any natural number. Then this tetrahedron in any does not contain any points inside
with integer coordinates, and on its border - lie just four points ,,, and no other.
Thus, the volume and surface area of the tetrahedron may be different, while the number of points within and on the boundary -
unchanged; therefore, the formula does not allow generalizations Pick even three-dimensional case.
● Center of mass of the system of points - ie all mass is concentrated only in the vertices of the polygon.
● Center of mass frame - ie weight polygon centered on its perimeter.
● Center of mass of the solid figures - ie weight polygon distributed throughout its area.
Each of these tasks is an independent decision, and will be considered separately below.
where - Mass points - their radius vectors (defining their position relative to the origin), and
- The desired radius vector of the center of mass.
In particular, if all the points have the same mass, the coordinates of the center of mass has
average coordinates of the points. For Triangle This point is called centroid
and coincides with the point of intersection of the medians:
For evidence these formulas is enough to recall that the equilibrium is reached at a point In which the sum of all the
forces is zero. In this case, it becomes the condition that the sum of the radius vectors of all points around , Multiplying by
the mass corresponding point is equal to zero:
Now we have the problem of a system of material points, and applying it to the solution of the
preceding paragraph, we find:
where - Point-to-mid Second side of a polygon, - Length Second hand - Perimeter, ie the sum of the lengths of the sides.
For Triangle can show the following statement: this point is point intersection of the bisectors triangle
formed by midpoints of the sides of the original triangle. (To show this, we need to use the above formula, and
then notice that the bisectors of a triangle divide the parties in the same proportions as the centers of mass of
these parties).
Triangle case
It is argued that for a triangle answer is still the same centroidIe point formed by the
arithmetic mean of the vertex coordinates:
Suppose now that the vector - vector from the vertex to the center of mass of the triangle number 1, and let the vector
- Vector drawn from to point (Which, recall, is the midpoint of the side on which it is):
Our goal - to show that the vector and collinear.
We denote and points that are the centers of mass of the triangles number 3 and number 4. Then,
obviously, the center of mass of the two triangles together to point , Which is the midpoint of .
Furthermore, the vector from point to point coincides with vector .
Seeking the center of mass Triangle lies in the middle of the segment connecting points and
(Since we broke triangle into two parts of equal areas: № 1 - № 2 and № 3 - № 4):
Thus, a vector from the vertex to the centroid equal. On the other hand, because the triangle similar to the
triangle number 1 coefficient Then the same vector is . Hence we obtain the equation:
Thus, we have proved that the vector and collinear, which means that the required centroid lies
on the median, coming from the top.
Moreover, in passing we have proved that the centroid divides each median in relation to Starting from the top.
which in case of equal mass is converted to the arithmetic average of coordinates of all points.
)
The intersection of a circle and a straight line
Decision
with geometric hand (And, due to this we get a more accurate solution in terms of numerical stability).
We assume without loss of generality that the center of the circle is at the origin (if it is not, then move it
back, correcting suitable constant C in the equation line). Ie have a circle with center (0,0) radius r and
straight to the equation Ax + By + C = 0.
First, we find closest to the center point direct - point to some coordinates (X0, y0). First, this point
must be at a distance from the origin:
| C |
----------
sqrt (A2 + B2)
Secondly, since the vector (A, B) perpendicular to the line, the coordinates of this point should be proportional
to the coordinates of the vector. Given that the distance from the origin to the desired point, we know we just
need to normalize the vector (A, B) to this length, and we get:
A C
x0 = ------
A2 + B2
B C
y0 = ------
A2 + B2
(There are not obvious signs only 'minus', but these formulas are easily verified by substituting
in the equation of the line - should get zero)
Knowing the closest to the center point of the circle, we can determine how many points will be the
answer, and even give an answer, if these points 0 or 1.
Indeed, if the distance (x0, y0) from the origin (its already expressed by us - see above)
larger than the radius, then answer - zero points. If this distance is equal to the radius, then the answer is one
point - (X0, y0). But in the remaining case, there will be two points, and their coordinates we will find it.
So we know that the point (x0, y0) lies inside the circle. Desired point (ax, ay) and (bx, by), in
addition to what should belong to the line, must lie on the same distance d from the point (x0,
y0), this distance is easy to find:
C2
d = sqrt (r2 ------) A2
+ B2
Note that the vector (-B, A) is collinear with the line, but because the required points (ax, ay) and (bx, by) can be obtained
by adding to the point (x0, y0) vector (-B, A), normalized to the length of d (we get a desired point), and subtracting the
same vector
(Obtain the second desired point).
d2
mult = sqrt (-----)
A2 + B2
ax = x0 + B mult
ay = y0 - A mult
bx = x0 - B mult
by = y0 + A mult
If we solved this problem in a purely algebraic, then most likely would have received a decision in another form, which gives more
accuracy. Therefore, the "geometric" approach described herein, in addition to clarity, and even more precise.
Implementation
As indicated at the outset, it is assumed that a circle is the origin. Therefore, the input parameters - the radius of the
double r, a, b, c; / / Input
Decision
Reduce our problem to one The intersection of a circle and a straight line.
We assume without loss of generality that the center of the first circle - at the origin (if it is not, then
transfer the center at the origin, and the derivation of the response will be back to add the coordinates
of the center). Then we have a system of two equations:
x2 + y2 = r12
(X - x2) 2 + (y - y2) 2 = r22
Subtract the second equation of the first to get rid of the squares of the variables:
x2 + y2 = r12
x (-2x2) + y (-2y2) + (x22 + y22 + r12 - r22) = 0
Thus, we have reduced the problem of the intersection of two circles to the problem of the intersection of the first circle
и the following line:
Ax + By + C = 0,
A =-2x2,
B =-2y2,
C = x22 + y22 + r12 - r22.
Description
Algorithm. Find the leftmost and rightmost points A and B (if several such points, then we take the very bottom of the left and right of
the top-most). It is clear that A, B and certainly fall within a convex hull. Next, draw through them straight AB, dividing the set of all
points on the upper and lower subsets S1 and S2 (the point lying on the line can be attributed to any set - they still will not be
included in the shell). Points A and
B assigned to both sets. Now we construct the upper shell for S1 and for S2 - lower shell, and combine them to get an answer.
To get, say, the top shell, you need to sort all the points on the abscissa, and then go through all the points, considering at
each step except for the point of the previous two terms included in the shell. If the current triple of points does not form a right
turn (which is easily verified by Oriented area) is the nearest neighbor to remove from the shell. Eventually remain only
point included in the convex hull.
Thus, the algorithm is to sort all points along the abscissa and two (worst case) rounds all points, ie,
the required asymptotic behavior of O (N log N) is reached.
Implementation
struct pt {
double x, y;
};
bool cw (pt a, pt b, pt c) {
return ax * (by-cy) + bx * (cy-ay) + cx * (ay-by) <0;
}
Decision
Here we consider the method vertical decompositionThat problems in the geometry is
often very important.
So, we have N triangles that can arbitrarily interfere with one another. Get rid of these intersections using vertical
decomposition: find all points of intersection of all segments
(Forming a triangle), and sort the results according to their point of abscissa. Suppose we have some array B. We move through
this array. At i-th step we consider the elements B [i] and B [i +1]. We have a vertical strip between the lines X = B [i] and X = B [i
+1], where, according to the very construction of the array B, inside this band segments can not intersect with each other.
Therefore, within this band triangles cut to trapezoids, and the sides of these trapezoids inside the strip do not overlap at all. Will
move from side to side
these trapezoids from the bottom up, and put the area of trapezoids, making sure that each piece was uchitan exactly once. In fact,
this process is very similar to the processing of nested parentheses. Adding the area of trapezoids within each band, and adding the
results for all the bands, and we will find the answer - the area of combining triangles.
Consider again the process of adding space trapezes already in terms of implementation. We iterate through all sides of the
triangles, and if any party (not vertical, we do not need the vertical sides, and on the contrary, will greatly disturb) falls into this
vertical strip (fully or partially), then we put it away in a vector , it's best to do it in this form: Y coordinate of the points of intersection
with the side boundaries of the vertical scroll, and the number of the triangle. After this, we constructed a vector containing pieces
sides sort it meaningfully Y: first the left Y, then the right Y. As a result, the first
vector element containing the underside of the lowest trapezoid. Now we just go to the resulting vector. Let i - the current item; this
means that the i-th piece - is some downside trapezoid some block (which can contain multiple trapezoids), which area we want to
add to the answer immediately. So we set a counter to 1 triangles, and climb up on the segments and increment the counter if we
find any side of the triangle for the first time, and decrease the counter, if we find a triangle for the second time. If at any interval j
counter becomes equal to zero, we
found the upper limit of the block - on this we stay, add the area of the trapezoid bounded by segments i
and j, i and assign j +1, and repeat the whole process again.
So, thanks to vertical decomposition method we solved the problem of geometric primitives
using only the intersection of two segments.
Implementation
struct segment {
int x1, y1, x2, y2;
};
struct item {
double y1, y2;
int triangle_id;
};
struct line {
int a, b, c;
};
void intersect (segment s1, segment s2, vector <point> & res) {
line l1 = {s1.y1-s1.y2, s1.x2-s1.x1, l1.a * s1.x1 + l1.b *
s1.y1}, l2 = {s2.y1-s2.y2, s2. x2-s2.x1, l2.a * s2.x1 +
l2.b * s2.y1};
double det1 = l1.a * l2.b - l1.b * l2.a;
if (abs (det1) <EPS) return;
point p = {(l1.c * 1.0 * l2.b - l1.b * 1.0 * l2.c) / det1,
(L1.a * 1.0 * l2.c - l1.c * 1.0 * l2.a) / det1};
if (px> = s1.x1-EPS && px <= s1.x2 + EPS && px> = s2.x1-EPS && px
<= s2.x2 + EPS)
res.push_back (p);
}
vector <item> c;
int main () {
int n;
cin >> n;
vector <segment> a (n *
3); for (int i = 0; i <n;
+ + i) {
int x1, y1, x2, y2, x3, y3;
scanf ("% d% d% d% d% d% d", & x1, & y1, & x2, &
y2, & x3, & y3); segment s1 = {x1, y1, x2, y2};
segment s2 = {x1, y1, x3,
y3}; segment s3 = {x2, y2,
x3, y3}; a [i * 3] = s1;
a [i * 3 +1] =
s2; a [i * 3
+2] = s3;
}
vector <point> b;
b.reserve (n * n *
3);
for (size_t i = 0; i <a.size (); + + i)
for (size_t j = i +1; j <a.size ();
+ + j) intersect (a [i], a
[j], b);
vector <double> xs (b.size ());
for (size_t i = 0; i <b.size ();
+ + i)
xs [i] = b [i]. x;
sort (xs.begin (), xs.end ());
xs.erase (unique (xs.begin (), xs.end (), & eq), xs.end ());
cout.precision (8);
cout << fixed << res;
}
Check points on the convex polygon
belonging
Given a convex polygon with N vertices, the coordinates of all vertices are integers (although it does not change the
essence of the decision); vertices are given in order of the counter-clockwise (otherwise you just need to sort them).
Receives requests - point, and is required to identify each point, it lies inside
this polygon or not (polygon boundaries included). For each request will respond in a mode on-line in O (log N).
Pretreatment of the polygon will be performed in O (N).
Algorithm
Will solve binary search on the corner.
One solution is as follows. Choose a point with the smallest coordinate X (if there are several, then choose the
lowest, ie, with the lowest Y). Concerning this point, we denote it Zero, all other vertices of the polygon lie in the
right half-plane. Furthermore, we note that all the vertices of the polygon is arranged in the corner relative to the
point Zero (this follows from the fact that a convex polygon, and is already ordered counterclockwise), and all
angles are in the range (-π / 2; π / 2].
Let receives another request - a point P. Consider its polar angle with respect to the point of Zero. Binary search will find two
such neighboring vertices L and R polygon that polar angle P lies between the polar angles L and R. Thus, we find that sector of
the polygon, in which lies the point P, and we only need to check whether the point P lies in the triangle (Zero, L, R). This can be
done, for example, using Oriented area of the triangle and Predicate "Clockwise", Just look at
clockwise or counter tops is a triple (R, L, P).
Thus, we in O (log N) sector we find a polygon, and then O (1) check the identity point of the triangle, and hence the
required asymptotic behavior is achieved. Pretreatment
Only the polygon is to predposchitat polar angles at all points, although these computations can be
carried on the same step of the binary search.
Implementation
This implementation assumes that there are no duplicate this polygon vertices and the
area of the polygon is nonzero.
struct pt {
int x, y;
};
struct ang {
int a, b;
};
int n;
cin >> n;
vector <pt> p
(n); int zero_id
= 0;
for (int i = 0; i <n; + + i) {
scanf ("% d% d", & p [i]. x, & p [i]. y);
if (p [i]. x <p [zero_id]. x | | p [i]. x == p [zero_id]. x && p [i]. y
<P [zero_id]. Y)
zero_id = i;
}
pt zero = p [zero_id];
rotate (p.begin (), p.begin () + zero_id, p.end
()); p.erase (p.begin ());
- N;
for (; ;) {
pt q; / / Another request
bool in = false;
if (q.x> = zero.x)
if (q.x == zero.x && q.y == zero.y)
in = true;
else {
ang my = {q.y-zero.y, q.x-zero.x};
if (my.a == 0)
my.b = my.b <0? 1: 1; vector <ang>
:: iterator it = upper_bound (a.
begin (), a.end (), my);
if (it == a.end () && my.a == a [n-1]. a &&
my.
b == a [n-1]. b)
it = a.end () -1;
if (it! = a.end () && it! = a.begin ())
{int p1 = int (it - a.begin ());
if (sq (p [p1], p [p1-1], q) <=
0)
in = true;
}
}
puts (in? "INSIDE": "OUTSIDE");
}
}
Finding the inscribed circle of a convex
polygon using the ternary search
Given a convex polygon with N vertices. Required to find the coordinates of the center and
radius of the largest inscribed circle.
It describes a simple method to solve this problem by using two ternary search working for O (N log2 C), where C -
coefficient and a coordinate value defined by the required accuracy (Cf. below).
Algorithm
We define the function Radius (X, Y)Returning radius of the inscribed circle in the given polygon with center
at (X; Y). It is assumed that X and Y lie within (or edge) of the polygon. Obviously, this feature is easy to
implement with the asymptotic O (N) - Just go through all the sides of the polygon, consider for each distance
to the center (where the distance can be taken as from direct to the point, is not necessarily regarded as a
segment), and return the minimum distance of the found - obviously it will be the largest radius.
So, we need to maximize this function. Note that, as a convex polygon, then this function is suitable for Ternary search in both
arguments: for a fixed X0 (of course, this,
that the line X = X0 crosses polygon) function Radius (X0, Y) as a function of one variable Y will first increase
and then decrease (again, we consider only the Y, the point (X0, Y)
the polygon). Moreover, the function max (at Y) {Radius (X, Y)} as a function of one argument X will first increase and
then decrease. These properties are clear from geometric considerations.
Thus, we need to do two ternary search: X and inside it to Y, maximizing the value of Radius. The only special
moment - you need to choose the right border of ternary searches as a function evaluation Radius outside the
polygon will be incorrect. To search for X no difficulty, simply choose the abscissa of the leftmost and rightmost
point. To search for a Y are those segments of the polygon, which gets the current X, and find the ordinates of
the points of these segments at the abscissa X (vertical segments is not considered).
It remains to estimate asymptotics. Let the maximum value that can take us - it is C1, and the required accuracy - about
10-C2, and let C = C1 + C2. Then the number of steps that will have to commit each ternary search, is the value of O (log
C), and the final asymptotic behavior is obtained: O (N log2 C).
Implementation
Constant steps determines the number of steps both ternary search.
In the implementation is worth noting that for each side immediately predposchityvayutsya coefficients in the equation line
и immediately normalized (divided by sqrt (A2 + B2)), to avoid unnecessary operations in the ternary search.
struct line {
double a, b, c;
};
int main () {
int n; vector
<pt> a (n);
Reading ... a ...
Given a convex polygon with vertices. Required to find the inscribed circle of maximum radius, ie, to
find the radius and center coordinates. (If at a given radius, multiple centers, it is sufficient to find any of
them.)
Unlike described here Ternary search of the double asymptotics of the algorithm - -
does not depend on the location and limits of the required accuracy, and therefore this algorithm is
suitable for considerably higher and more restrictions on the value of the coordinates.
Thank you Ivan Krasil'nikov (Mf) for this beautiful description of the algorithm.
Algorithm
So, given a convex polygon. Begin simultaneously with the same speed shift all its sides are
parallel to each other inside the polygon:
Suppose, for convenience, this movement occurs at a rate of 1 unit per second coordinate (ie the time in some sense equal to the
distance: later point in time, each unit will overcome a distance equal to one).
During this movement of the polygon will gradually fade (contact point). Sooner or later the whole polygon
shrinks to a point or a segment, and this time will be answer Problem - desired radius (And the center of
the desired circle will lie in this interval). In fact, if we squeezed polygon thickness in all directions, and he
appealed to the point / segment, then it means that there is a point distant from all sides of the polygon at a
distance And for long distances - such a point does not exist.
So, we need to learn how to effectively simulate the process of compression. To learn this for
each side determine the timeThrough which it will shrink to a point.
For this, consider carefully the process of moving parties. Note that the vertices of the polygon always move along the
bisectors of angles (this follows from the equality of the corresponding triangles). But then the question of time, through which
the compressed side, reduced to the question of determining the height triangle, which is known side length and two
adjacent thereto angle and. Using, for example, the sine theorem,
we obtain the formula:
Now we know for determine the time in which the party will shrink to a point.
These times are listed for each side in some structure for extracting data minimum,
eg red-black tree ( in the language of C + +).
Now, if we extract the side with the least time , This side of the first compressed to the point - in time . If a polygon has
not shrunk to the point / segment, then this aspect should remove of the polygon, and continue the algorithm for the
remaining sides. When you delete a part, we need to join
each other of its left and right neighbors, extending them to their point of intersection. This will need to find
this point of intersection, recalculate the length of two sides and their times of extinction.
When implemented for each side will have to keep the number of its right and left neighbor (and thus
how to build a doubly linked list of sides of the polygon). This allows for the removal of the binding of
the two sides and its neighbors for .
If you delete the part is that its side-neighbors parallel, This means that the polygon is then
compression point degenerates in / cut, so we can immediately stop the algorithm, and to return as a
response time of disappearance current side (so that problems do not arise parallel sides).
If such a situation does not arise parallel sides, the algorithm to finalize the moment in which a
polygon will be only two parties - and then the answer to the problem will be the removal of the
previous hand.
Obviously, the asymptotic behavior of this algorithm is because the algorithm consists of steps, each of which is removed
Implementation
We present the implementation of the algorithm described above. This implementation returns only the
desired radius of the circle; however, the addition of O center of the circle will not be hard.
This elegant algorithm that from computational geometry requires only finding the angle between the two
sides, the intersection of two lines and two lines check for parallelism.
Note. It is assumed that the signal input to the polygon - strictly convexIe no three points lie on one
line.
struct line {
double a, b, c;
line (const pt & p, const pt & q) {
a = p.y - q.y;
b = q.x - p.x;
c = - a * p.x - b * p.y;
double z = sqrt (a * a + b *
b); a / = z, b / = z, c / =
z;
}
};
struct cmp {
bool operator() (const pair <double,int> & A, const pair
<double,int> & B) const {
if (abs (a.first - b.first) > EPS)
return a.first <b.first;
return a.second <b.second;
}
};
int main() {
int n;
vector <pt> p;
Reading ... n and p ...
double last_time;
while (q.size() > 2) {
last_time = q.begin()->
First; int i = q.begin()->
Second; q.erase (q.begin());
next[prev[i]] = Next[i];
prev[next[i]] = Prev[i];
int nxt = next[i], Nxt1 = (nxt +1)% N, prv
= prev[i], Prv1 = (prv +1)% N;
if (parallel (line (p[nxt], P[nxt1]), Line (p[prv], P[prv1])))
break;
q.erase (make_pair (h[nxt], Nxt));
q.erase (make_pair (h[prv], Prv));
h[nxt] = Get_h ( p[nxt],
P[nxt1],
P[prv1], P[prv],
p[next[nxt]], P[(next[nxt]+1)% N]
);
h[prv] = Get_h ( p[prv],
P[prv1],
p[(prev[prv]+1)% N], P[prev[prv]],
p[nxt], P[nxt1]
);
The main function here - it That on the side and its left and right neighbors calculates
time of disappearance of the hand. Sought for this intersection point of this side with the neighbors,
and then the above formula calculation is made of the desired time.
Voronoi diagram in 2D
Definition
Dana points plane. Consider the partition of the plane into areas
(Called Voronoi polygons or Voronoi cells, sometimes - polygons proximity cells Dirichlet partition Thyssen), where - the set
of all points in the plane that are closer to the point
Than to any other point :
Needless partition of the plane is called a Voronoi diagram given set of points .
Here - Given metric, usually the standard Euclidean metric: but below will be
considered and the case so
called the Manhattan metric. Here and below, unless otherwise specified, will be considered for
the Euclidean metric
Voronoi cells are convex polygons, some are endless.
Points belonging to the definition of multiple cells at once Voronoi and usually relate directly to
several cells (in the case of Euclidean metric set of points of measure zero, in the case of the
Manhattan metric bit more complicated).
These polygons were first studied in depth by the Russian mathematician Voronoi (1868-1908 gg.).
Properties
● Voronoi diagram is a planar graph, so it has vertices and edges.
Application
Voronoi diagram is a compact data structure that stores all the information needed to solve many
problems of intimacy.
As discussed below, it is the time required to build most of the Voronoi diagram, in the asymptotics
is not considered.
● Finding the nearest point for each.
Note the simple fact that if the nearest point is the point, this point is "their" in the cell edge
. It follows that to find for each point nearest to it is enough to see her ribs Voronoi cell. However, each edge belongs to
exactly two cells, so it looked exactly twice,
and due to the linearity of the number of edges we obtain a solution for this problem .
● Finding the convex hull.
Recall that the vertex belongs to the convex hull if and only if its Voronoi cell is infinite. Then we find in the Voronoi diagram any
infinite edge and begin to move in a fixed direction (eg, counterclockwise) on the cell containing this edge until we reach
until the next infinite edges. Then go through this edge in the adjacent cell and continue to crawl. In
result, all viewed ribs (except infinite) will be sought by the parties of the convex hull.
Obviously, the run time - .
● Finding the Euclidean minimum spanning tree.
Find the minimum spanning tree with vertices at these points Connecting all these points. If we apply the standard
methods of graph theory, then, because graph in this case has no edges,
even the optimal algorithm will have no less asymptotics.
Consider the graph dual Voronoi diagram, ie Delaunay triangulation. It can be shown that the presence of Euclidean
minimum spanning tree is equivalent to constructing the core Delaunay triangulation. Indeed, Prim's algorithm each time
sought the shortest edge between two points mozhestvami; if we fix
one set point, the point closest to it has an edge in the Voronoi cell, so the Delaunay triangulation will
present an edge to the nearest point, as required.
Triangulation is a planar graph, ie by a linear edges, so it is possible to apply Kruskal's algorithm and
an algorithm with running time .
● Finding the largest empty circle.
You want to find the largest radius circle that does not contain any of the points inside (center of the circle must lie inside the
convex hull points). Note that because the largest radius of curvature function at this point is strictly monotonic within each
Voronoi cell, it reaches its maximum
at one of the vertices of the Voronoi diagram, or at the intersection of ribs and a convex hull diagram (the number
of such points is more than twice the number of edges of the diagram). Thus, it remains only to sort out these
points and for each to find the nearest, ie solution for .
Consideration should start with simple case - a case of two points and .
If or, the Voronoi diagram for them to be respectively vertical or horizontal line.
Otherwise Voronoi diagram will look like "corner": Cut at an angle degrees in the rectangle formed by the
points and And the horizontal / vertical beams of the end thereof depending on whether the longer side of
the rectangle of the vertical or horizontal.
Special case - when this rectangle has the same length and width, i.e. .
In this case, there will be two infinite regions ("corners" formed by two rays parallel to the axes), which by definition must belong
to both of the cells. In this case, further comprises determining
in the condition as it is understood these areas (sometimes artificially introduced a rule that every corner
referred to his cell).
Thus, even for two points Voronoi diagram in this metric is a non-trivial object, and in the case of a larger number of
points, these figures need to be able to quickly cross.
Finding all the faces, the outer edge
of a planar graph
Dan planar stacked on a plane graph with vertices. Want to find all its facets. Face is the
part of the plane bounded by the edges of the graph.
One of the faces will be different from others in that it will have an infinite area, such a face is called the outer face.
Some problems need to find only the outer edge, the algorithm for finding which, as we shall see, in fact no different
from the algorithm for all faces.
Euler's theorem
We present here a few Euler's theorem and its corollaries, of which it will follow that the number of edges and faces of
a planar simple (without loops and multiple edges) of the graph are of the order .
Let a planar graph is connected. We denote the number of vertices in the graph, - The
number of edges - the number of faces. Then we have Euler's theorem:
Prove this formula easily follows. In the case of wood ( ) Formula is easily verified. If the graph - not a tree, then
remove any edge belonging to any cycle; wherein the amount
not change. We will repeat this process until you come to a tree for which the
identity is already installed. Thus, the theorem is proved.
Consequence. For any planar graph let - The number of connected components. Then we have:
Ie .
If the graph is not connected, then summing the resulting estimates of its connected
components, we again obtain , As required.
Consequence. Number of faces simple planar graph is a value . This follows from
So we learned to circumvent one face, starting from any edge on its boundary. You're starting to learn to choose the
edges so that the resulting faces are not repeated. Note that each edge two different ways in which it can get: Each of
them will receive their faces. On the other hand, it is clear that one is oriented edge belongs to exactly one edge.
Thus, if we will mark all edges each detected faces from an array, and do not run circumvention of already labeled
edges, then we will bypass all the faces (including foreign), even just once.
Present directly implementation this bypass. We assume that the graph adjacency lists already
ordered by the corner, and multiple edges and loops are absent.
A first embodiment of easy, the next node in the adjacency list he is looking for a simple search. Such an implementation is
theoretically works for, although in practice many tests, it works very quickly
(With a hidden constant, significantly less than unity).
Another embodiment of a more streamlined - enjoys the fact that the top of the list in order of adjacency corner. If you implement a
function comparing two points in the polar angle with respect to the third point (eg by issuing it as a class, as in the
example below), the search terms in the adjacency list, you can use binary search. As a result, we obtain a realization for
class cmp_ang {
int center;
public:
cmp_ang (int center) : Center(center)
{}
bool operator() (int a, int b) const {
Should return ... trueIf a point is smaller than b
polar angle relative to center ...
}
};
And possible option based on container , Because we only need to quickly learn the position numbers
in the array. Of course, such an implementation would also work .
It should be noted that the algorithm is not quite working correctly with isolated peaks - such peaks he
just does not detect as individual faces, though, from a mathematical point of view, they must be a
single connected component and faces.
In addition, special edge is outside face. As distinguished from its "normal" faces, described in the next
section. It should be noted that if the graph is not connected, the external face will consist of several circuits,
and each of these circuits will be found by the algorithm separately.
// consider the
area double area =
0;
// add a dummy point for simplicity
counting area
facet.push_back (facet[0]);
for (size_t k =0; k +1<Facet.size(); + + K)
area + = (p[facet[k]]. First + p[facet[k
+1]]. First)
*(p[facet[k]]. Second - p[facet[k
+1]]. Second);
if (area <EPS)
The outer face is ... ...
}
course, making sure that we have not added loops). After completing this process for all segments, ie for We
struct point {
double x, y;
bool operator < (const point & p) const {
return x <p.x - EPS | | abs (x - p.x) <EPS && y <p.y - EPS;
}
};
map <point,int>
Ids; vector <point>
p;
vector <vector <int>> G;
int main() {
// input int m;
vector <pair <point,point>> a (m);
Reading ... ...
// Graphing
for (int i =0; i <m; + +
I) { vector
<point> cur;
for (int j =0; j <m; + + J)
intersect (a[i], A[j], Cur);
sort (cur.begin(), Cur.end());
for (size_t j =0; j +1<Cur.size(); + + J) {
int x = get_id (cur[j]), Y = get_id (cur[j +1]);
if (x! = y) {
g[x]. Push_back
(y); g[y].
Push_back (x);
}
}
}
int n = (int) g.size();
/ / Sort by angle and removing multiple edges
for (int i =0; i <n; + + I) {
sort (g[i]. Begin(), G[i]. End(), Cmp_ang (i));
g[i]. Erase (unique (g[i]. Begin(), G[i]. End()), G[i]. End());
}
}
Finding the closest-pair
Trivial algorithm - through all the pairs and calculating the distance for each - for works .
. This algorithm has been proposed
The following describes the algorithm, the running time preparations
(Preparata) in 1975 Drug and Shamos also revealed that the decision tree model, this algorithm
asymptotically optimal.
Algorithm
Construct an algorithm for the general scheme of algorithms "Divide-and-conquer": Algorithm executed in the form of a
recursive function, which transmits a plurality of pixels; This recursive function is the set of splits in half, calling itself
recursively on each half, and then performs any business combination answers. Merge operation is to detect when a
single point
Optimal solutions fell into one half, and another point - to another (in this case, a recursive call of each of the halves
individually detect the pair, of course, can not.) The main difficulty, as always, lies in the effective implementation of this stage
of the association. If recursive function transmits a plurality of points, then the stage of integration should work no more
than, then the asymptotic behavior of the whole algorithm
will be given by:
Then we take the average after sorting point (), And all point to it and very referred to the
first half, and all points afterwards - in the second half of:
, We will find an
Now called recursively on each of the sets and answers d for each
halves. Take the best of them: .
Now we need to make the step of combiningIe try to find such a pair of points,
And the other -
distance between which is less than, one point lies in the in . Obviously, for
it is sufficient to consider only those points which are spaced from the vertical line section is not
distance minimal, i.e. many considered at this stage points is:
For each point of the set must try to find the points that are closer to it than . Eg
it suffices to consider only those points whose coordinates differ by no more than . Furthermore, no
make sense to consider those points which Coordinate more Coordinates of the current point. Thus,
for each point define the set of points under consideration follows:
If we sort the points of on-coordinate, then find will be very easy: it is somewhat
points straight to the point.
So, in the new notation stage of integration as follows: to construct a set
, Sort it according to terms -Coordinate, then for each point consider all the points
And each pair Calculate the distance and compare it with the current best distance.
At first glance, it is still sub-optimal algorithm: it seems that the size of the sets are of the order ,
and the required asymptotic behavior does not work. However, surprisingly, we can prove that the size of each set
is the value that is does not exceed a certain small constant irrespective
points themselves. The proof is given in the next section.
Finally, pay attention to the sorting algorithm described above which contains just two: first sorting
pairs (,), and then sorting the elements of on. In fact, both of these sorts
inside the recursive function can be eliminated (otherwise we would not have reached
evaluation for the steps of combining,
asymptotic behavior of the algorithm and the total
would have been ). From the first sort is easy to get rid -
sufficiently in advance of the launch of recursion to perform this sort: in fact inside recursion elements themselves do not
change, so there is no need to sort again. With the second sorting little harder to fulfill its pre-fail. But, remembering
mergesort (Merge sort), which also works on the principle of divide-and-conquer, you can simply embed this sort in our
recursion. Let recursion, taking a set of points (as we recall, ordered pairs
) Returns the same set, but already sorted coordinate . To do this, simply merge (for) two
results returned by recursive calls. Thereby get sorted by set.
Rating asymptotics
To show that the above algorithm is indeed satisfied for It remains
prove the following fact: .
So, let us consider some point; Recall that the set - A set of points, -
And, moreover, in the and the point
coordinate is no more But not less coordinate itself And all
points of the set lie in a strip . In other words, that we consider the point and
lie in the rectangle size .
Our task - to estimate the maximum number of points that can be in this rectangle ;
thus we estimate the maximum size of the set and (It is one less, because in
This rectangle is also a point). At the same time we must not forget that in the general case can occur
and duplicate points.
an ,
Recall that obtained as the result of at least two recursive calls - from sets d Wherein
contains a point on the left of the line section and partially - The remaining points of the line section and point to
thereon, the right
therefrom. For any pair of points As well as of , The distance can not be less than - or would
it meant incorrectness recursive function.
To estimate the maximum number of points in the rectangle divide it into two squares To
first put all square point And the second - the rest, ie . Of
the above considerations, it follows that in each of these squares distance between any two points does not exceed
. But then this means that in each square is not more than four points?
Indeed, suppose there is a square And the distance between any two points does not exceed the same
. We prove that the square can not be more than 4 points. For example, this can be done as follows:
divide this square into 4 squares with sides . Then in each of these small squares can not
That is less
be more than one point (because even diagonal equal than ). Consequently, squared way around
can not be more than 4 points.
So, we have proved that in a rectangle can not be more points and, therefore,
size of the set can not exceed , As required.
Implementation
We introduce a data structure for storing point (its coordinates and a certain number) and comparison
operators are needed for the two types of sorting:
struct pt {
int x, y, id;
};
pt a[MAXN];
double mindist;
int ansa, ansb;
inline void upd_ans (const pt & a, const pt & b) {
double dist = sqrt ((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + .0);
if (dist <mindist)
mindist = dist, ansa = a.id, ansb = b.id;
}
Finally, the implementation of most of the recursion. It is assumed that before calling the array is already sorted by
-Coordinate. Recursion is simply passed two pointers Which indicate that it should seek an answer to. If the
distance between and too small, the recursion must stop and perform
trivial algorithm to find the nearest pair and then sort on the subarray -Coordinate.
To merge two sets of points obtained from the recursive call, one (ordered by-coordinate)
we use the standard STL function And create a secondary buffer (One to all
recursive calls). (Use inexpedient, since it generally does not work
in linear time).
Finally, the set stored in the same array .
int m = (l + r) >> 1;
int midx = a[m]. X;
rec (l, m), Rec (m +1,
R);static pt t[MAXN];
merge (a + l, a + m +1, A + m +1, A + r +1,
T, & cmp_y); copy (t, t + r-l +1, A + l);
int tsz = 0;
for (int i = l; i <= r; + + I)
if (abs (a[i]. X - midx) <Mindist) {
for (int j = tsz-1; j> =0 && A[i]. Y - t[j]. Y <mindist;
- J)
upd_ans (a[i], T[j]);
t[tsz + +] = A[i];
}
}
By the way, if all the coordinates are integers, then for the duration of recursion can never move to
fractional values, and stored in squared minimum distance.
In the main program should call recursion as follows:
sort (a, a + n, &
cmp_x); mindist =
1E20;
rec (0, N-1);
Definition
Fix a circle centered at the radius . Then inversion point on this circle is called a point Which lies on the line
And a distance condition is imposed:
If we assume that the center of the circle coincides with the origin, we can say that the point has the
is the polar angle, and that, as the distance calculated by the above formula.
In terms of complex numbers inversion transformation is expressed quite simply, if we assume that
the center circle coincides with the origin:
Application of inversion (at mid-board) to the image of a chessboard provides an interesting picture (right):
Properties
Obviously, any point lying on the circumference, With respect to which the inversion transformation, when displaying
a same goes. Any point lying inside circumference
passes into external region, and vice versa. It is believed that the center point of the circle becomes
"infinite" And point "infinity" - on the contrary, at the center of a circle:
Obviously, the repeated use of the inversion transformation draws her first application - all
points are returned:
Generalized circle
Generalized circle - this is either a circle or a straight line (it is believed that this is also a
circle, but having infinite radius).
A key property of the inversion transformation - that when applied generalized circle
always translate into a generalized circle (Assuming that the transformation pointwise
inversion is applied to all points in the figure).
Now we'll see what happens with the lines and circles in the conversion of inversion.
Consider any point this line, and we also consider the point - the nearest point of the line. It is clear that the
segment perpendicular to the line, but because the angle formed by them - Direct.
We now use Lemma Equal anglesThat we shall prove later, this lemma gives us the equality
Therefore, the angle also direct. As we take any point, it turns out that the point lies
as diameter. Easy to understand that in the end, all points on the line will cover
on the circle, built on the entire
this circle as a whole, hence the assertion.
Formulation
Consider any two points and, and apply to them the inversion transformation, we obtain the
points and. Then the following angles are equal:
Proof
Prove that the triangle and similar (the order of vertices is important!).
Thus, the triangles have a common angle, and the two sides adjacent thereto are proportional, therefore, these
triangles are similar, and therefore the same respective angles.
Consequence of Lemma
If there are any three points , , , The point lies on the segment Then performed:
these angles being oriented in opposite directions (i.e., when viewed as the two angle-oriented, then
they are of opposite sign).
In the implementation of the last transition we changed the order of the points, which means
that we have changed the orientation angle of the opposite.
Conformality
Inversion transformation is conformal, ie preserves angles at points of intersection curves.
Thus, if the angles considered as oriented, the orientation angles in the application inversion is
reversed.
For evidence this, consider two arbitrary curves intersect at and having it tangents. Suppose on the first point of the curve
will go , On the second - the point (We we let them in the limit ).
Obviously, after the application of inversion curves will continue to intersect (unless, of course, they do not pass
through the point But such a case we do not see), and their intersection point is .
Given that the point lies on the line joining and we find that we can apply the corollary to Lemma
Equal angles from which we obtain:
where under the sign "minus" we mean that the angles are oriented in different directions.
Letting terms and to point , In the limit we obtain that equality - an expression of
the angle between the intersecting curves, as required.
Reflection property
If - Generalized circle, then when you convert it inversion saved if and only if orthogonal circle
with respect to which the inversion (and considered to be different).
The proof of this property is interesting because it demonstrates application of geometric inversion to
avoid the circles and the task.
The first step evidence is an indication of the fact that and have at least two points
intersection. In fact, the inversion transformation displays in the interior of the circle
her appearance, and vice versa.
Time after the conversion has not changed, it means that it contains both points
from the interior and the exterior of the circle. This implies that the points of intersection of the two (the one she can not
may - which means two touching circles, but in this case, obviously, be the condition can
not exist; match circumferentially and are not, by definition.)
Denote one point of intersection through , Another - through. Consider a circle with center
point and perform the inversion transformation for her. Note that if and circle, and
generalized circle must pass intersecting lines. Given the conformity
an
inversion transformation, we find that d coincide if and only if the angle between two
these intersecting straight line (in fact, the first inversion transformation - respect,
- Changes the direction of the angle between the circles on the opposite, so if the circle coincides with its inversion, the
angles between intersecting lines on both sides must be the same and equal
degrees).
Practical application
Immediately it should be noted that when used in the calculations must take into account a large errorIntroduced by
transformation inversion may appear very small fractional number of orders, and is usually due to high error inversion
method works well only with relatively small coordinates.
a certain point ), And then the desired circle will have a diameter .
Suppose now that we want to find the circumference, resulting in another circle of inversion. Generally speaking, the
center of the new circle - not coincides with the center of the old circle. To determine the center of the new circle can take
this concept: to navigate through the inversion center of the circle and the center of the old line, to see her point of
intersection with the old circle - let it be and point. Segment forms a circle diameter of the old, and easy to understand that
after the inversion of this segment will continue to be
form diameter. Consequently, the center of the new circle can be found as the arithmetic average points and .
where
Mnemonically these formulas can remember this: the circle center moves "almost" as to transform inversion, only the denominator
Ie if the problem is quite complicated operations with various circles, it makes sense to apply to the
input data inversion transformation, try to solve the problem without resulting modified circles (or a
smaller number of them), and then re-use inversion to obtain the solution of the original problem.
An example of this problem is described in the next section.
Steiner chain
Given two circles and one is strictly inside the other. Then, the third circle is drawn regarding these two circles, whereupon
an iterative process is started each time a new circle is drawn so that it concerned previous painted, and the first two.
Sooner or later, another draw a circle intersect with one of the previous set, or at least touch it.
Case of the intersection:
Touch case:
Accordingly, our task - to put as much as possible circles, so that the intersection (i.e., the first of
the presented cases) were not. The first two circles (external and internal) are fixed, we can only
vary the position of the first relating to the circle, then everything is uniquely placed on the
circumference.
If you touch the receiving chain circles called Steiner chain.
With this so-called chain of linked approval Steiner (Steiner's porism): if there is at least one set of Steiner (ie,
there is a corresponding provision relating to starting a circle, leading to a chain of Steiner), then for any other
choice regarding starting circle will also receive a Steiner chain, and the number of circles in her will same.
From this statement it follows that in the solution to maximize the number of circles the answer
does not depend on the position of the first set of the circle.
Proof and constructive algorithm for solving the following. Note that the problem has a very
simple solution in the case where centers of the outer and inner circles coincide. Clearly, in this case
The number of circles set does not depend on the first set. In this case, all circles have the same radius, and their number and
the coordinates of the centers can be calculated using simple formulas.
To go to this simple situation of any supplied to the input, apply the inversion transformation with respect to a circle.
We need the center of the inner circle and moved coincided with the center of the outer, so look for a point with
respect to which we take the inversion, it is necessary only on the line connecting the centers of the circles. Using the
formula for the coordinates of the center of the circle after
use inversion can equate the position of the center of inversion, and to solve this equation. Thus we have from any
situation can go to a simple, symmetric case, and, having solved the problem for him, re-apply the inversion
transformation and obtain the solution of the original problem.
.
an
d
, , , . Point moves along a circle passing through the point.
a
n
For evidence we note first that the points d lie on a line (this follows from
a
point of intersection of the n
equality of triangles). We denote segments d . We introduce the notation:
Algorithm
For simplicity of the algorithm, we assume without loss of generality that the center of the first circle has coordinates
. (If not, then this can be achieved by a simple shift of the whole picture, and after finding a solution
- The shift of their direct back.)
We denote and the radii of the first and second circles, and by - Coordinates of the center of
the second circle (point is different from the origin, as we do not consider the case when the same
circle, or one circle is inside the other).
To solve the problem come to clean it algebraically. We need to find all lines of the form, which lie at
a distance from the origin, and the distance from point .
In addition, we impose the condition of normalization direct sum of the squares of the coefficients and should be equal to
one (it must, otherwise the same straight line will correspond to an infinitely many views species ).
To get rid of the modules, we note that all there are four ways to uncover modules in this system. All
of these methods can be considered a general case, if we understand how disclosure of the module
that the coefficient on the right side may be multiplied by .
In other words, we come to such a system:
Introducing the notation and We arrive at the fact that four times the system must address:
The solution of this system is reduced to solving a quadratic equation. We omit all the tedious
calculations, and immediately give a ready answer:
Overall we've got solutions instead . But it is easy to understand, in what place there extraneous solutions: in fact, in the
latter system is sufficient to take only one solution (eg, the first). In fact, the geometric meaning of what we take and clear:
we actually sort out which side
from each of the circles will direct. So two ways arising in the solution of the latter system, redundant:
just select one of two solutions (only, of course, in all four cases, select one and the same family of
solutions).
The last thing we have not yet considered - it as a direct shift in the case where the first circle does not initially was at the
origin. However, here is simple: the linearity of the line equation that
ratio of should take the value of (Wherein and - Coordinates of the center of the original
the first circle).
Implementation
We first describe all the necessary data structures and other auxiliary definitions:
struct pt {
double x, y;
pt operator- (pt p) {
pt res = { x-p.x, y-p.y
};return res;
}
};
struct circle: pt {
double r;
};
struct line {
double a, b, c;
};
Then the decision itself can be written in such a way (where the main function to call - the second: and the first function
- Auxiliary):
void tangents (pt c, double r1, double r2, vector <line> & ans)
{ double r = r2 - r1;
double z = sqr(c.x) + Sqr(c.y);
double d = z - sqr(r);
if (d <-EPS) return; d
= sqrt (abs (d)); line
l;
l.a = (c.x * r + c.y * d) / Z;
l.b = (c.y * r - c.x * d) / Z;
l.c = r1;
ans.push_back (l);
}
Algorithm
We draw a vertical line mentally and start moving this straight right. In the course of its motion this line will
meet with the segments, and at any given time each segment will intersect with our direct at one point (we'll consider
that there is no vertical segments).
Thus, for each segment at some point of time it will appear on the scanning line, then will move straight ahead and this
point, and finally, at some point disappear straight segment.
We are interested in the relative order of the segments vertically. Namely, we will keep a list of
segments intersecting the scanning line at a time where cuts will be sorted by their Coordinate on-line
scanning.
This order is interesting in that overlapping segments will have the same -Coordinate of the at
least one time:
Formulate key statements:
● To find intersecting pairs is sufficient to consider for each fixed position scanning line Only adjacent
segments.
● It suffices to consider the scanning line is not valid in all possible positions ,
andonly in those positions, when new segments or disappear
old. In other words, enough to confine only provisions equal abscissas-
ends of the segments.
● When a new segment is sufficient insert it in the right place in the list obtained for a previous
scanning line. Should be checked for intersection only added to the segment its immediate
neighbors in the list, the top and bottom.
● With the disappearance of the segment enough remove from its current list. After this you
should verify at the intersection with the upper and lower neighbors list.
● Other changes in the order of the segments in the list, other than as described, does not exist.
Other checks to make crossing is not necessary.
To understand the truth of these assertions are the following observations:
● Two disjoint segment never change their relative order.
В Indeed, if one segment was initially higher than the other, and then became lower,
between these two moments was the intersection of these two segments.
● Have matching Coordinates two disjoint segment also can not.
● From this it follows that at the moment when we find a segment in the queue position of this segment,
and rearrange over this segment does not have the queue, his order relative to other segments
queue will not change.
● Two intersecting segments at their point of intersection will be neighbors each other in turn.
● Therefore, to find pairs of intersecting segments enough to cross check all the only pair of segments
that ever during the motion of the scanning line at least once were neighbors of each other.
Easy to see that it is enough just to check the added segment with its upper and lower neighbors, as well as removing the
segment - its upper and lower neighbors (which, after removal become neighbors of each other).
● It should be noted that for a fixed position scanning line we first must produce addition all
segments appearing here, and only then - removal all endangered segments here.
Thus, we will not miss intersection of the segments on top: ie such cases when the two
segments have a common vertex.
● Note that vertical segments in fact does not affect the correctness of the algorithm.
These segments are allocated in order that they appear and disappear at the same time. However, due to the
previous remark, we know that the first all segments will be added to the queue, and only then will be removed.
Consequently, if the vertical line segment intersects with any other open at this time segment (including vertical),
then this is detected.
В what place queued vertical segments? After the vertical segment has no one specific
Coordinates, it extends to the whole segment -Coordinate. But it is easy to understand that
as - Coordinates can take any coordinate of this segment.
Thus, the entire algorithm will make no more tests on the intersection of a pair of segments, and make operations
with the queue lengths (by operations in moments of appearance and disappearance of each segment).
struct pt {
double x, y;
};
struct seg {
pt p, q;
int id;
double get_y (double x) const {
if (abs (p.x - q.x) <EPS) return p.y;
return p.y + (q.y - p.y) * (x - p.x) / (q.x - p.x);
}
};
inline bool intersect1d (double l1, double r1, double l2, double r2) {
if (l1> r1) swap (l1, r1);
if (l2> r2) swap (l2, r2);
return max (l1, l2) <= Min (r1, r2) + EPS;
}
struct event {
double x;
int tp, id;
event() {}
event (double x, int tp, int id) :
X(x), Tp(tp), Id(id)
{}
set <seg> s;
vector <set <seg> :: iterator> where;
s.clear();
where.resize (a.size());
for (size_t i =0; i <e.size(); + +
I) { int id = e[i]. Id;
if (e[i]. Tp == +1) { set
<seg> :: iterator
nxt = s.lower_bound (a[id]),
Prv = prev (nxt);
if (nxt! = s.end() && Intersect (* Nxt,
a[id])) return make_pair (nxt-> id,
id);
if (prv! = s.end() && Intersect (* Prv,
a[id])) return make_pair (prv-> id,
id);
where[id] = S.insert (nxt, a[id]);
}
else {
set <seg> :: iterator
nxt = next (where[id]),
Prv = prev (where[id]);
if (nxt! = s.end() && Prv! = S.end() &&
intersect (* Nxt, * prv))
return make_pair (prv-> id, nxt->
id); s.erase (where[id]);
}
}
The main function here - That returns numbers found intersecting segments
or If no intersection.
Checking for the intersection of two segments are supplied by the By algorithm
based on the oriented area of the triangle.
Queue lengths in the global variable - . Iterators indicating the position
each segment in the queue (for easy removal from the queue lengths), are stored in the global array .
Also introduced two auxiliary functions and Which returns an iterator to the previous
and the following elements (or If one does not exist).
Constant denotes the error of comparing two real numbers (mostly it is used
checking two segments at the intersection).
Z-row function and its calculation
Suppose given a string length . Then Z-function ("Z-function") from this line - an array of length , Trader
element is equal to the greatest number of characters, starting position Coinciding with the first character . In other
Examples
An example, as computed Z-function for multiple lines:
● :
● :
● :
Trivial algorithm
Formal definition can be represented as the following elementary realization for :
We just for each position sort out the answer for her Starting with zero, and as long as we do not
a mismatch is found or we reach the end of the line.
Of course, this implementation is too inefficient, we now turn to the construction of an efficient algorithm.
Then we can use the already calculated previous Z-values of the function to initialize the value
not zero, and maybe some big number.
For this we note that the substring and coincide. This means in that the
An
initial approximation for we can take the corresponding value in the interval d
namely, the value
of .
However, the value to be too large: so that when it is applied to the position it
"Come out" of bounds . This can not be allowed because about the characters to the right we
know nothing, and they may differ from those required.
We present example such a situation, for example, line:
When we get to the last position ( ), The current segment will be the rightmost . Items
, Wherein the response
Given this interval corresponds to the position is . Obviously,
this value to initialize impossible, it is completely incorrect. The maximum value of what we
could initialize - it Because it is the largest value that is not beyond the interval climbs . Thus, as the initial
Implementation
Implementation turns out rather laconic:
Array is initially filled with zeros. Current rightmost segment is assumed to be a match, ie, deliberately
small segment, which does not get any .
Inside the loop on we first by the above algorithm determines the initial value
- Or it will remain zero, or calculated based on the formula above.
Thereafter, the trivial algorithm that attempts to increase the value of as much as possible.
In the end, the current update of the rightmost segment coincidence, of course, if this
update is required - ie if .
We are interested in a nested loop - Because everything else - the only constant operations performed times.
We show that each iteration This cycle lead to an increase in the right border unit.
In this case, we initialize the value of the above formula, a certain number of . Compare this to
the initial value with the value Obtain three choices:
Applications
Consider several uses Z-functions for specific tasks. These applications will be largely
Row compression
Given a string length . Required to find the shortest its "compressed" representation, ie find the following line
shortest length that can be represented as a concatenation of one or more copies of .
To solve calculate Z-line function And find the first position such that And wherein
divided into . Then the line can be compressed to length line .
Proof of such a decision does not differ from that of a solution using prefix function.
For example, the string "abcabcd" prefix function is: Which means:
● the row "a" no nontrivial prefix matching the suffix;
● in line "ab" no nontrivial prefix that matches the suffix;
● the row "abc" no nontrivial prefix matching the suffix;
● the row "abca" prefix length coincides with the suffix;
● the row "abcab" prefix length coincides with the suffix;
● the row "abcabc" prefix length coincides with the suffix;
● the row "abcabcd" no nontrivial prefix that matches the suffix.
Trivial algorithm
Immediately following the definition, it is possible to write such an algorithm for computing the prefix function:
Efficient algorithm
This algorithm was developed by Knuth (Knuth) and Pratt (Pratt) and independently by Morris (Morris) in
1977 (as a basic element for the search algorithm of the substring).
The first important point - that the value of no more than one unit exceeds the value any .
(This scheme again the same curly brackets denote the same substring)
Suppose now that, on the contrary, it appears that . Then we need to try and try
substring at length. In order to optimize I would like to go directly to a (maximum) length ,
that still holds the prefix property position Ie :
a
n
Indeed, when we find such a length, we will again be sufficient to compare the characters d
- If they match, it can be argued that . Otherwise we will have to find again
minimal (the next largest) value for which the prefix property, and so on.
It may happen that such values run out - this happens when . In this case,
if Then Otherwise .
Thus, the general scheme of the algorithm we already have, but remained unresolved question of finding such effective
lengt
hs . We pose this question formally, at the current position and length (for which the prefix property
ie ) Is required to find the greatest Such that in
still holds the prefix property:
After such a detailed description is practically begs that this value is nothing, as the value of the prefix function, which
has been calculated previously (subtraction unit appears because
0-indexed rows). Thus, finding these lengths we can for each.
● Read the values of the prefix function will in turn from to (Meaning just
assign zero).
● For calculation of the current value we led a variable Indicating the current length of
treated sample. Initially .
an
● Test sample length, which compare characters d . If they match - is
believe and proceed to the next index . If the characters are different, the decrease
length Setting it equal And repeat this step of the algorithm from the beginning.
● When we reached the length and did not find a match, then the process stops busting samples and
believe and proceed to the next index .
Implementation
Algorithm eventually turned in a surprisingly simple and concise:
As you can see, this algorithm is online algorithm, i.e. it processes the data in the course of income - can, for example,
to read the string one character at once and handle this character, finding the answer to the next position. The
algorithm requires storage of the string itself and the previous calculated values prefix function, however, is easy to
see if we know beforehand the maximum value that can take a prefix function on the whole line, it is enough to store
only one more than the number of the first character and values prefix function.
Applications
text and line Required to find and display the positions of all occurrences of the string in text .
For convenience through length of the string And through - Length of the text .
Form the string Where the symbol - a separator that should not anywhere else
meet. Count for this string prefix function. Now consider its value, but the first
(Which, as seen, to include row and separator). By definition, the value of
shows of the longest length of the substring, ending at position and coinciding with the prefix. But in this case - in fact the
length of the largest block matching with the string and ending at position
. More than , This length can not be - at the expense of the separator. But equality (Where
It is achieved), means that the position occurrence of the string ends with the title (Just do not
forget that all positions are measured in line glued ).
Thus, if at any position turned , The gap
Thus, the algorithm Knuth-Morris-Pratt solves this problem for time and memory.
other string And is required for each prefix count how many times it occurs in .
We solve the first problem first. Consider in any position meaning of the prefix function in it. By definition, it means that
the position occurrence of the string ends with the prefix length And
no greater prefix end at position can not. At the same time, in positions could terminate and
occurrence of prefixes smaller lengths (and, obviously, not necessarily length ). However, as
easy to see that we have come to the same question, to which we have already answered when considering
algorithm
prefix computation functions for a given length I must say, what of the longest of its own suffix
coincides with its prefix. We have already seen that the answer to this question will . But then in this task,
if the position is terminated occurrence of a string of length Coinciding with the prefix, as in
occurrence of a substring ends length Coinciding with the prefix, and it applies to those
same arguments, so also ends, and the occurrence of length etc. (until the index
becomes zero). Thus, for calculating the response, we need to perform such a cycle:
Here, for each value of the prefix function, first count how many times he had met in the array and
then considered a somewhat dynamic, if we know that the prefix length occurs exactly once,
what exactly this number must be added to the number of occurrences of its own suffix length, coinciding with its prefix; then have this
suffix (of course, less than length) executed "bouncing"
This amounts to their suffix, etc.
Now consider the second problem. We apply a standard trick: assign to a row line through the separator, ie
is, we get the string And calculate it for the prefix function. The only difference from the first objective will
that it is only necessary to consider those values of the prefix functions which relate to the line, i.e. all
for .
Take line and invert it (write the characters in reverse order). Our task -
count how many rows such prefixes that are not found anywhere else in it. But if we think
string prefix function and find its maximum value , Then obviously occurs in a row (not
в beginning) of its length prefix But not greater length. Clearly, a shorter length prefixes certainly meet
в it.
So, we have found that the number of new substrings that appear when append
symbol Equal .
Thus, for each symbol is appended for we can count the number of different substrings. Thus, for we can find a
number of different substrings for any given line.
It is worth noting that you can completely analogous to recalculate the number of different substrings and
append a character in the beginning, as well as removing the character from the end or the beginning.
Row compression
Given a string length . Required to find the shortest its "compressed" representation, ie find the following line
shortest length that can be represented as a concatenation of one or more copies of .
It is clear that the problem is in finding the length of the search string . Knowing the length of the
answer to the problem would be, for example, the prefix string this length.
Count on row prefix function. Consider its last value, ie And introduce
designation . We show that if divisible by, then it will be a long answer,
otherwise efficient compression does not exist, and the answer is.
Indeed, let divided into. Then, the string can be represented as a series of blocks of length, and,
by definition, the prefix function, prefix length will coincide with its suffix.
really fit the answer.
We show that this response is optimal. Indeed, otherwise, if there was a minimal, then the prefix and function at
the end would be more than Ie a contradiction.
Suppose now that not divisible by. We show that this implies that the length of the response is . We prove by contradiction
- Assume that the answer exists and is of length ( divider ). Note that the prefix function must necessarily be more Ie
this suffix should partially cover the first block. Now consider the second block row; because coincides with the prefix suffix and prefix
and suffix cover this unit, and
their displacement relative to each other does not divide the length of the block
(as would otherwise shared), all characters
block coincide. But then the line consists of the same character here And the answer must exist, ie
is, so we arrive at a contradiction.
prefix function for her, and for all subsequent characters prefix function to compute on the fly:
Indeed, in this situation, knowing the next character and the value of the prefix function in the
previous item, you can calculate a new value-prefix function does is not using all the previous
characters in the string prefix and values in their function.
In other words, we can construct automatic: State it will be the current value of the prefix function, the
transitions from one state to another will be effected under the symbol:
Thus, even without a line We can construct a pre-transition table using the
same prefix computation algorithm functions:
s + = '#';
int n = (int) s.length();
vector <int> Pi = prefix_function (s);
vector <vector <int>> Aut (n, vector
<int>(alphabet));for (int i =0; i <n; + + I)
for (char c =0; c <alphabet; + +
C) { int j = i;
while (j> 0 && C! = S[j])
j = pi[j-1];
if (c == s[j]) + +
J;
aut[i] [c] = J;
}
However, in this case, the algorithm will work for (- Cardinality of the alphabet). But note that
instead of the internal cycle Which gradually shortens response, we can use already
calculated part of the table: moving from values to the value We actually say that the transition
And for him the answer is already
from state lead to the same state as transition accurately counted (ie
k )
s + = '#';
int n = (int) s.length(); vector
<int> Pi = prefix_function (s);
vector <vector <int>> Aut (n, vector <int>(alphabet));
for (int i =0; i <n; + + I)
for (char c =0; c <alphabet; +
+ C) if (i> 0 && C! =
S[i])
aut[i] [c] = Aut[pi[i-1]] [c];
else
aut[i] [c] = I + (c == s[i]);
The result was a very simple implementation of the construction of the machine working for .
Can be useful when a machine? To begin with we recall that we consider the prefix function for strings And its
value is usually used for the sole purpose to find all occurrences of the string in row .
Therefore, the most obvious benefit of constructing such a machine - acceleration calculation prefix
function row . Constructing a row machine, we no longer need
no string Nor value-prefix function in it, and do not need no calculations - all transitions (ie how
will change the prefix function) predposchitany already in the table.
But there is a second, less obvious use. This is the case when the line is a giant line, built under a
rule. This can be eg Gray Line or line formed by recursive combination of several short lines, submitted
for entry.
Suppose for definiteness that we solve such a task: Given a room Gray line, and a line with
. Required to count the number of occurrences of the
length string in Gray-th row. Recall line
Gray defined as follows:
In such cases, even just building line will be impossible due to its astronomical length (eg, -
Gray th row has length ). Nevertheless, we can calculate the value of the prefix function at the end
this line, knowing the value of the prefix function, which was before the line.
So, apart from the machine also calculate such quantities: - The value of the machine achieved
after "feeding" him a line if before this machine was in a state . The second value -
- The number of occurrences of
the string in line if to "feeding" this line machine is in state
. In fact, - The number of times that the automatic take a value for
time "feeding" lines. It is clear that the answer to the problem would be the value .
How to calculate these values? First, the baseline values are. And all subsequent values can be calculated
by using the previous values and automatic. So, to calculate
plus the first character of the
these values for some, we remember that the line comprises alphabet plus
again . Then after "feeding" the first piece ( ) Machine to a state Then
after "feeding" symbol it will enter:
So we have solved the problem for strings Gray can similarly solve a class of such problems. For example, exactly the same method,
we solve the next challenge: Given a string , And samples, each of which is defined as follows: a string of normal characters,
which can occur among the recursive inserting other
in the form of rows , Which means that in this place should be inserted instances of the string.
Example of such a
scheme:
Guaranteed that this description does not contain cyclic dependencies. Constraints are such that if explicitly disclose
In most applications, it makes sense to calculate all the necessary power P in any array.
/ / Sort by hashes
sort (hashes.begin (), hashes.end ());
/ / Output response
for (int i = 0, group = 0; i <n; + + i)
{
if (i == 0 | | hashes [i]. first! = hashes [i-1].
first) cout << "\ nGroup" << + + group <<
":";
cout << '' << hashes [i]. second;
}
where:
However, there's an easier way. In most cases, instead of sharing hashes raising at P, we
can reverse these multiply their power.
Suppose given two hash: one multiplied by P [I], and the other - on the P [J]. If I <J, then multiply Stationery hash P [JI], or else
we multiply the second hash P [IJ]. Now we are led to a degree of hashes, and can compare them quietly.
For example, the code that calculates hashes of all prefixes and then O (1) compares two strings:
Application hashing
Here are some typical uses hashing:
● Rabin-Karp algorithm for the substring search for O (N)
● Determination of the number of distinct substrings in O (N ^ 2 log N) (see below)
● Determination of the number of palindromes in a string
int result = 0;
Implementation
string s, t; / / Input
// consider a hash of
strings S long long h_s =
0;
for (size_t i = 0; i <s.length (); + + i)
h_s + = (s [i] - 'a' + 1) * p_pow [i];
Reverse Polish Notation was developed by the Australian philosopher and expert in the theory of computing
machines Charles Hamblin in the mid-1950s on the basis of Polish notation, which was proposed in 1920 by
the Polish mathematician Jan Lukasiewicz.
Ease of RPN is that the expressions presented in a form very easily calculate, and in linear time. Head stack initially it
is empty. Will move from left to right the expression in reverse polish notation; if the current element - a number or a
variable, then put on top of the stack of its value; if the current element - an operation that took out the top two elements
of the stack (or one if unary operation), to apply the operation and put the result back on the stack. In the end, the stack
will be exactly one element - the value of the expression.
Thus, we have learned to evaluate the expression for, and thus we implicitly used the
Reverse Polish Notation: we placed the operation in such a manner, when the time of calculating the next
operation both its operands have already been computed. Slightly modifying the above algorithm can obtain an
expression in reverse polish notation and explicitly.
Unary operations
Now suppose that the expression contains a unary operation (ie one argument). For example, are
particularly common unary plus and minus.
One difference in this case is the need to determine whether the current operation is a unary or
binary.
You may notice that before unary operation is always either another operation, or an opening parenthesis,
or nothing at all (if it stands at the beginning of the line). Before the binary operation, in contrast, is always
either operand (number / variable) or a closing parenthesis. Thus, it is enough to have some flag to indicate
whether the next operation to be unary or not.
More net realizable subtlety - how to distinguish between binary and unary operation when removed from the stack and
computation. You can, for example, for unary operations instead of the symbol put on the stack .
Priority for unary operations should be chosen such that it was more of the priorities of all binary operations.
In addition, it should be noted that the unary operations are actually right associative - if row are several unary
operations, they must be processed from right to left (for a description of this case, see below; code here already
accounts for right-associativity).
It is worth noting that in the simplest cases, for example, when a unary operations are allowed only and right-
associativity has no role, therefore, in such situations, no complications in the scheme can be
not to impose. Ie cycle:
Right-associativity
Right-associativity of an operator means that operators with equal priority are evaluated from right to
left (respectively, left associativity - when left to right).
As noted above, unary operators are usually right associative. Another example -
usually exponentiation considered right-associative (really, a ^ b ^ c is generally perceived as a ^ (b ^ c),
and not (a ^ b) ^ c).
What are the differences should be made to the algorithm to correctly handle right-associativity? In fact, the most
minimal changes are needed. The only difference will be shown only at
equality priorities, and it is that the operations of equal priority that are on top of the stack should not be
used to perform the current operation.
Thus, the only difference should be made to function calc:
Building for
Strictly speaking, the algorithm described below will not perform sorting suffixes and cyclic shifts line. However, this algorithm
is easy to obtain and suffix sorting algorithm: enough to attribute end of the line in any character which is certainly less than
any of the characters, which may consist of
string (for example, it may be a dollar or Sharp, C language for this purpose, you can use an
existing null character).
Immediately, we note that since we sort of cyclic shifts, then we will substring
consider cyclic: A substring When Understood
substring . In addition, all pre-indices are taken modulo the length of the line
(In order to simplify the formulas, I will omit the explicit taking indices modulo).
phases. At the second
The issue before us algorithm consists of approximately phase ( )
sorted cyclic substring length. At the last, The second phase will be sorted substring
length , Which is equivalent to sorting cyclic shifts.
In addition to every phase permutation algorithm cyclic substring indexes will
support for each cyclic substring starting at the position with a length ,room
equivalence class, which belongs to this substring. In fact, of substrings can be identical, and the algorithm will
need information about it. In addition, the numbers of the classes of equivalence
will give so that they retain the order information and, if a suffix is less than the other, the class number and
he must get smaller. Classes will be numbered for ease from scratch. Number of equivalence classes will be
stored in the variable .
an
We present example. Consider the string . Array values d at each stage with zero
on the second as follows:
It is worth noting that an array of ambiguity possible. For example, zero-phase array can
equal: . Then what option will depend on the specific implementation of the algorithm, but
all options are equally valid. At the same time, in an array no ambiguity could not be. We now
On zero phase we need to sort the cyclic length of the substring Ie individual characters of the string and divide them into
equivalence classes (just the same symbol must be assigned to the same equivalence class). This can be done trivially, for
example, sorting, counting. For each character's count how many times he met. Then restore the information on this array.
After
, the passage through the array and comparing the character array is constructed .
int p[maxlen], Cnt[maxlen], C[maxlen];
memset (cnt, 0, Alphabet * sizeof(int));
for (int i =0; i <n; + +
I)
+ +
Cnt[s[i]];
for (int i =1; i <alphabet; + + I)
cnt[i] + = Cnt[i-1];
for (int i =0; i <n; + +
I)
p[- Cnt[s[i]]] = I;
c[p[0]] = 0;
int classes = 1;
for (int i =1; i <n; + +
I) {
if (s[p[i]] ! = S[p[i-1]]) + + Classes;
c[p[i]] = Classes-1;
}
Further, suppose we have
fulfilled Nd phase (ie, calculated values of arrays and for her) now learn
for perform the following, Th, phase. Since all phases , It will give us
required algorithm with time .
consists of two substrings of
For this we note that the length of the cyclic substring length We
can compare with each other for Using information from the previous phase - the rooms
equivalence classes. Thus, for the length of the substring starting at the position , All
the information contained in a pair of numbers (Again, we use an array
the previous phases).
This gives us a very simple solution: sort substring length just by by that pairs of numbersIt will give us the
desired order, ie, array. However, the conventional sort that runs in time
, We are not satisfied - it will give an algorithm for constructing suffix array with the
time (but this algorithm is somewhat easier to write than described below).
How quickly perform the sorting pairs? Since the elements of pairs do not exceed , Then you can sort by
count. However, to achieve the best hidden in the asymptotic constant instead of sorting couples come to
just sort of numbers.
Here we use the technique, which is based on the so-called cell sorting:
to sort couples sort them first by the second element, and then - the first elements of (but certainly stable sorting, ie, does not
violate the relative order of elements with equal).
from the previous
However, a separate second elements are already in order - this order specified in the array phase.
Then, to arrange for a second pair of elements, you just have to each array element take -
this will give us the sort order for the second element pairs (after gives ordering substring length And
the transition to the line twice the length of the substring these become their second halves, so from
position of the second half of the first half length is subtracted).
Thus, with just a subtraction of elements of the array we produce sorting by
The second element pairs. Now you have to produce a stable sort on the first elements of pairs, it is
can be done in using counting sort.
It remains only to count numbers equivalence classes, but they were easy to get, just passing by
received a new permutation and comparing adjacent elements (again, comparing two numbers as a pair).
We present implementation Algorithm execution of all the phases other than zero. Introduced additional temporary
arrays and ( - Contains a permutation in the sort order for the second element pairs
- New numbers of equivalence classes).
Applications
The above algorithm produces a sort of cyclic shifts (if the line does not ascribe to the dollar), but because give
inside a binary search can be made trivial for . Then the asymptotic behavior of the substring search in the text
becomes .
We construct the suffix array for, while maintaining intermediate results: we need arrays of each
Thus, the implementation is roughly this (it is assumed here that the calling procedure itself generates as do it in constant time is not so
easy (apparently, most likely - predposchetom), but
Anyway, it has no relation to the use of suffix array):
We construct the suffix array for, while maintaining intermediate results: we need arrays of each
The basis for this algorithm is the following idea: find some way most common prefixes for each
neighboring pairs in sorted order of suffixes. In other words, we construct an array, where is the
greatest common prefix and suffix. This
array will give us the answer to any two adjacent rows of suffixes. Then the answer for any two suffixes, not necessarily
adjacent, you can get through this array. In fact, even received a request from some
an - Their position in the
numbers and suffixes . Find these codes in the suffix array, ie let d array
(We order them, ie let ). Then the response to this request will be a minimum in the array Taken
can be replaced with
on the interval . In fact, the transition from the suffix the suffix whole
chain transitions, starting with the suffix and ending in the suffix But comprising
all intermediate suffixes are in the sort order between them.
Thus, if we have such an array , The answer to any request longest common prefix reduces
to request minimum on the interval array . This classical problem on the minimum interval (range
minimum query, RMQ) has multiple solutions with different asymptotic behavior described here.
So, our main task - construction this array . We will build it in the course of the algorithm
suffix array construction: at each iteration, the current will build an array for cyclic
substring of the current
length.
After a goalless array iteration Obviously, should be zero.
Suppose now that we have And should be in the
completed -Th iteration, received from her array current Th
. As we remember, in the
iteration recalculate the array, it received a new meaning algorithm
constructing suffix array cyclic substring length broke in half into two substrings of length
; use this same technique to create the pattern and .
So, even for the current iteration algorithm for computing the suffix array has done his job, found a new meaning permutation
substrings. We will now go through this array and see a pair of adjacent substrings:
и ,. Each substring breaking in half, we have two different situations:
1) The first half of substrings at the positions and are different, and 2) the first match halves
(Remember, this comparison can be easily produced by simply comparing the numbers of
classes the previous
iteration). Let us consider each of these cases separately.
1) First half substrings differ. Note that while in the previous step, these must first halves were nearby. In fact, the
equivalence classes could not disappear (and can only
appear), so all the different length of the substring give (as the first halves) of the current
iteration different substrings of length and in the same order. Thus, for determining in it
case you should just take the appropriate value from the array .
2) First half match. Then the second half could be the same or different and; while if they are different, then they do not
necessarily have to be adjacent in the previous iteration. Therefore, in this case, there is no easy way to determine. To
define it, we must do the same, as we
then going to calculate the greatest common prefix of any two suffixes: minimum necessary to
execute the query (RMQ) on the corresponding interval array .
We estimate the asymptotics this algorithm. As we have seen in the analysis of these two cases, only the second case
gives increase in the number of equivalence classes. In other words, we can say that every new
equivalence class appears with a single query RMQ. Since all equivalence classes can
to be, then we should at least look for the asymptotics . And for this we need to use what is already-
what data structure to the lowest point on the segment; this data structure will have to be rebuilt on each
iteration (which only ). A good option is a data structure Segment tree: Its
can be constructed in And then perform searches for That just gives us the final
asymptotics .
Implementation:
For any two suffixes length of their longest common prefix can now be found at least in the
relevant segment of the array :
Using the fact that suffixes have already sorted, it is easy to understand that the current suffix give in
as new substring all their prefixes, except match the prefix suffix . Ie all
its prefixes, except First, give the new substring. Since the length of the current suffix is
, We finally obtain that the current suffix gives new
substrings. Summing it all suffixes (for the very first, , Take nothing - will be added
just ), We obtain answer Problem:
On an intuitive level, suffix automaton can be understood as concise information about all substring this string.
Impressive fact is suffix automaton that contains all
information in a compressed form so that the length of the string, it requires only memory. Moreover, it
may also be constructed during (If we consider the size of the alphabet constant; otherwise -
during ).
Historically, first linearity size suffix automaton was opened in 1983 of Blumer et al, and 1985
- 1986. were presented the first algorithms for its construction in linear time (Crochemore, Blumer
et al.) For more details - see the list of references at the end of the article.
English suffix automaton is called "suffix automaton" (in the plural - "suffix automata"), and directed acyclic graph of
words - "directed acyclic word graph" (or simply "DAWG").
● One of the states is called initial stateAnd it must be the source of the graph (ie reachable from it
all other states).
● Each transition in the machine - this arc, labeled by some symbol. All transitions originating from a
state are required to have different tags. (On the other hand, the state transitions need not be for
some symbols.)
● One or more states labeled as terminal states. If we pass from
initial state along any path to a terminal state, and write with the labels of all arcs traversed, you get a string
that is bound to be one of the suffix string .
● Suffix automaton contains the minimum number of vertices among all machines that satisfy the above conditions.
(Minimum number of transitions is not required as subject to a minimum number of states in the machine can not be
"extra" ways - otherwise it would violate the previous property.)
For a line :
For a line :
For a line :
For a line :
For a line :
Proof. Assume that a plurality of and have at least one common element.
Then it means that the line and terminate at the same location, i.e. - Suffix . But then every
occurrence of the string contains at its end occurrence of the string That means that its set
entirely embedded in many .
Lemma 3. Consider a class -Equivalence. Sort all substrings included in
this class in nonincreasing length. Then the resulting sequence each substring will be one shorter than the
previous one, and still be a suffix of the previous. In other words,
substring belonging to one equivalence class are actually suffixes each other and take all
kinds of different lengths in an interval .
Proof.
Fix an equivalence class. If it contains only one row, then the correctness of the lemma is obvious.
Suppose now that the number of rows is more than one.
According to Lemma 1, two different -Equivalent strings are always such that one is
proper suffix another. Consequently, in the same class -Equivalence can not be
lines of equal length.
We denote length, and through - The shortest line in this equivalence class. According to Lemma
1, the line is a proper suffix of. We now consider any suffix string in length
segment , And show that it is contained in the same equivalence class. In the
fact, this suffix can be included in Only as a suffix string (as shorter suffix
includes only a suffix string ). Consequently, according to Lemma 1, this suffix -
equivalent line, as required.
Suffix links
Consider a state machine . As we now know as corresponds to some
string class with the same values Moreover, if we denote by longest of these lines, the
all others will be suffixes.
We also know that the first few lines of suffixes (If we consider the suffixes in descending order
their length) are in the same equivalence class, and all other suffixes (minimum blank
suffix) - in some other classes. We denote first such suffix - in it and we will carry out
suffix link.
In other words, suffix link leads to a state which
corresponds of the longest suffix line, which is in a different class -Equivalence.
Here we assume that the initial state corresponds to a single equivalence class (containing
only an empty string), and we believe .
Lemma 4. Suffix links form tree, whose root is the initial state .
Proof. Consider an arbitrary state . Suffix link leads THEREOF
state, which correspond to lines of strictly smaller length (this follows from the definition of suffix links and
Lemma 3). Therefore, moving on suffix links, we will sooner or later come from the state in the initial state
, Which corresponds to an empty string.
Lemma 5. If we construct a set of all available tree (Principle "Set-
parent contains as a subset of all of their children "), it will be the same as the structure of the tree
suffix links.
Proof.
What sets of You can construct a tree from Lemma 2 (that any two sets
either not intersect, or one contained in the other).
Now consider an arbitrary state and the suffix link . From the definition
suffix links and Lemma 2:
which together with the previous lemma proves our assertion: suffix tree reference is essentially a
timber is laid sets .
Here is an example tree suffix links in suffix automaton built for the line :
Subtotal
Before proceeding to the very algorithm systematize knowledge accumulated above and
introduce a couple of auxiliary notation.
● Set of substrings can be divided into equivalence classes according to their sets closure .
● Suffix automaton consists of an initial state , As well as one of each class
-Equivalence.
● If we start from an arbitrary state and we will go on suffix links, then sooner or later we get to the initial state. In
this case we get a sequence of disjoint intervals That in the union will give one
continuous segment.
However, there is a complexity in order to maintain the state of the suffix link . We need to spend a suffix link to a
state in which the length of the string will be just this very
Ie for this state must be equal . However, such a state could and
not exist: in this case we need to make "Splitting" state.
● Thus, one possible scenario, a continuous transition turned, i.e. . In
this case is simple, no cleavage is not necessary to produce, and we just spend a suffix link
from state in the state.
● Another more sophisticated version - when a discontinuous transition, ie . This means
that of corresponds not only necessary to us substring length But also
substring greater length. We have no choice but to make "Splitting" state Smash the segment
rows corresponding to her two subsegments, so the first will end up just long .
How to produce this split? We "Clone" state Making a copy of it with
all the transitions, because we do not
parameter . We copy the of want
no way to change the path passed through. Suffix link from we are wherever led
old suffix link from, and a link from aim in .
After cloning, we spend a suffix link from in - Something for which we cloned.
The last remaining step - redirect some members of the transitions, forwarding them on. What exactly must redirect incoming
transitions? Just redirect only the transitions corresponding
all suffixes line Ie we must continue to move on suffix links, starting from the top,
and as long as we get to a fictitious state or we reach a state of transition which leads
in a state other than.
dynamic list (for fast failover of all available keys). Thus we reach time of the algorithm, but at the cost
memory consumption.
Thus, we assume a constant alphabet size, ie each search operation on the symbol transition, add transition, searching for
● First place - it is run on the state of the suffix links with the addition of ribs on the symbol .
struct state {
int len, link; map
<char,int> Next;
};
Suffix machine itself will be stored in an array of these structures . As proved in the next section, if
- Is the maximum possible length of a line in the program, it is enough to have a memory for
the states. We also keep a variable - state corresponding whole line at the moment.
Let function initializes Suffix Machine (creating machine with a single initial state):
void sa_init() {
sz = last = 0;
st[0]. Len = 0;
st[0]. Link = -
1; + + Sz;
/ * / / This code is only needed if the machine is built for
many times
different lines:
for (int i = 0; i <MAXLEN * 2;
+ + i) st [i].
next.clear ();
* /
}
Finally, we present the implementation of the basic functions - which adds another character to
the end of the current line, rearranging appropriately courier:
It has an interior vertex with one son, it means that this son does not contain at least
one number parent; then create a virtual vertex with , Equal to this number, and
gain this son to the parent. As a result, we obtain a tree in which each internal node has
degree is greater than one, and the number of leaves does not
exceed . Consequently, only such a tree is not a
top. Given the initial state, which may be the same other
state, we obtain the estimate . However, this estimate may slightly improve, noting that only on test
form happens that there is a state other than the initial, which comprises
all the numbers from to ; but on this test score clearly can not be achieved and, therefore, we have
improved the final evaluation to (Due to the fact that no longer allow one of the initial state - in all the
other tests the initial state is no longer the same as the other vertex).
Thus we have shown that assessment independently, without knowledge of the algorithm.
It is interesting to note that this estimate is sharp, ie there test on which it is achieved. This test is as
follows:
When the processing of the rows on each iteration, beginning with the third split state will occur, and
thereby can be reached score .
corresponding path length from the initial state in And - Long way from in any terminal state. On the one hand, all
these lines for all non-continuous transitions are different (because the strings and only continuous
transitions are formed). On the other hand, each of these rows By definition the terminal state, the entire
string is suffixed .
Since the non-empty suffixes have line only pieces, and then the entire string Among these lines
could not be contained (as the whole line corresponds to the path of solid edges),
then the total number of non-continuous transitions does not exceed .
Adding these two estimates, we obtain the estimate . However, remembering that the
maximum number of states can only be achieved on the test species, and it is clearly not
achieved score, obtain the final estimate , As required.
It is interesting to note that there is also test which this bound is attained:
Communication with suffix tree. Construction of suffix tree
by suffix automaton and vice versa
Prove two theorems that establish mutual relationship between suffix automaton and suffix tree.
Outset that we believe that the input string is that each has its own suffix in the suffix tree top (as for
arbitrary strings, generally speaking, is not true: for example, for the line). Usually this is achieved
by assigning to end of line for some special character (usually denoted by a dollar sign).
For convenience, we introduce the notation: - A string Written in reverse order - this suffix
automaton built for the line , - Is suffix tree line .
We introduce the concept expanding links: Fix the top suffix tree and the symbol ; then extending the link
leads to the top of the tree, the corresponding line (If this path
ends in the middle of the edge, then draw a link to the lower end of the rib); if such a path
is not in the tree, then extends link is not defined. In a sense, expanding links opposite suffix
links.
These two theorems allow one of the structures (suffix tree or suffix automaton) to build another during - These two
simple algorithms will be discussed below us in Theorems 3 and 4.
For illustrative purposes, let suffix tree automaton with its suffix links and the corresponding suffix tree for the inverted row.
suffix tree and its links (for clarity, we sign each state
its -Line):
:
Lemma. The following three statements are equivalent for any two substrings and :
● in row
● in row
● and lie on the same path from the root to the suffix tree .
Proof it's pretty obvious that if the beginning of occurrences of two strings are the same, then one string is a
prefix of another, which means that one line lies in the suffix tree in the way of another string.
Proof of Theorem 1.
Suffix automaton states correspond to the vertices suffix tree.
QED.
.
.
.
it is a condition which corresponds to the substring from which (in text ) Coincides with
substring from .
It just means that:
The first part of the theorem, it remains to prove the second part: that all solid transitions in the machine correspond to
the links in the suffix tree. Continuous transition is different from those of non-exhaustive,
that Ie after attributing characters we were in a state with
line, the maximum of the equivalence class of this state. This means that in the calculation
corresponding spreading links We immediately hit the top of the tree, and did not go down
down to the nearest tree tops. Thus, assigning a single character in the beginning, we were top of the tree to
another - so if it is inverted suffix link in the tree.
Suffix tree will contain the same number of vertices as the states in , Wherein
top of the tree, resulting from the state automaton corresponds to a string of length .
According to Theorem 1, the edges in the tree formed as inverted suffix links, and arc labels can be found on the basis of the
difference between the states, and further knowing each state machine any one element of his set (This is one
Thus, during the time we can construct a suffix tree with a suffix link in it.
(If we consider the size of alphabet is not constant, then all will take time to rebuild .)
Proof of Theorem 4.
Suffix automaton will contain the same number of states as vertices . Do
each state its longest string will correspond to an inverted way of
root of the tree to the top.
According to Theorem 2, to build all the transitions in the suffix automaton, we need to find all the
links extend .
First, note that some of these links extend obtained directly from the links in the suffix tree. In fact, if for any vertex We
consider it a suffix link, it means
that it is necessary to hold the link extends from in the first character string corresponding to the vertex .
However, since we do not find all the links extend. Additionally, it is necessary to go through the suffix tree from the leaves to the root,
and for each vertex see all her sons, each son see all extend
references And copy this link to the top, if this symbol from the top link is not yet
was found:
Checking entry
Condition. Given a text And receives requests in the form: given a string Required to verify whether
or not the line is included in text as a substring.
the algorithm will run much faster, without processing the entire string as a whole.
ie can be expressed as the sum of responses to all possible transitions from state .
Answer to the problem is the value of (Unit is subtracted to ignore empty substring).
Asymptotics. .
Decision. Solution of the problem similar to the previous, Only now it is necessary to consider the
dynamics of two values: the number of different substrings and the total length .
How to count , Described in the previous task, and the value of can be calculated as follows:
ie we take the answer for each vertex, and add to it Thus like to start attributing
each row one character.
Asymptotics. .
Decision. Construct suffix automaton for the string . Then this machine will contain both ways
all cyclic shifts of the string.
Consequently, the problem reduces to that found in the machine to the lexicographically minimal path
length That is done in a trivial way: we start in the initial state and each time
act greedily, passing the transition with minimal character.
Number of occurrences
Condition. Given a text And receives requests in the form: given a string , Want to know how many
times the line text included in the as a substring (entry may overlap).
Asymptotics. Preprocessing and one request.
all states in order of their length Mapped and the current value suffix on the following link:
It is alleged that in the end we did calculate for each state the correct values .
Why is this true? Total states obtained by cloning is not, exactly And th of them
appeared when we added the first characters. Consequently, each of these states, we associate this
position at which processing it appeared. So initially each such condition And all other states
.
Then we perform for each such an operation: . The point of this is to
that if the string corresponding to the state , Met times, then all of its suffixes will
meet the same.
Why thus we do not take into account the same position a few times? Because each state of its value "Mapped"
only once, so there could well be that one of his state to "icing" to some other state twice, in two different ways.
So we learned to count these values for all states of the automaton.
After that prompted trivial - just return Wherein - The state corresponding to the model .
constructing an automaton for each state position the end of the first occurrence.
struct state {
...
bool is_clon;
int first_pos;
vector <int> Inv_link;
};
Answer to the problem will be equal, and the line itself can be restored by restoring the manner in
which the dynamics turned this minimum.
Now let us consider the symbol and want to recalculate the answer for him.
● If the state of in the machine there is a transition from the symbol, we simply accomplish this transition and increase
unit.
● If the state of not have the required transition, we should try to shorten the current matching part,
which should go to suffix link:
In this case it is necessary to shorten the current length, but leave as possible. Obviously, this should be
assigned to, because after a rush down the suffix link us to satisfy any substring
length corresponding to that of:
If from the new state will not go back to the desired character, then again, we should go on and reduce the suffix link, and so on, until
we find the transition (then go to step 1) or we do not get
Implementation:
Literature
We first give a list of the first works related to suffix automata:
● A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler, R. McConnell. Linear Size Finite Automata for
the Set of All Subwords of a Word. An Outline of Results [1983]
● A. Blumer, J. Blumer, A. Ehrenfeucht, D. Haussler. The Smallest Automaton Recognizing
the Subwords of a Text [1984]
● Maxime Crochemore. Optimal Factor Transducers [1985]
● Maxime Crochemore. Transducers and Repetitions [1986]
● A. Nerode. Linear automaton transformations [1958]
In addition, more contemporary sources, this issue is addressed in many books on string algorithms:
Clarification of statement
Clearly, in the worst case, such substring-palindromes can be , And at first glance it seems
that algorithm with linear asymptotic behavior can not exist.
However, information can be found palindromes return more densely: For each
an
position we find the values d Denoting the number of palindromes
respectively odd and even length with center position .
For example, in row There are three odd length palindrome with center symbol Ie
value :
In line There are two palindrome of even length with center symbol Ie value :
Ie idea - is that if there is a palindrome length with center at any position, there is also
subpalindromes length , Etc. with centers. Therefore, two such arrays and enough
to store information about all subpalindromes this line.
Rather unexpected fact is that there is a fairly simple algorithm that calculates these "arrays palindrome" and in
linear time. This algorithm is described in this article.
Decision
Generally speaking, the problem has several well-known solutions: using hashing technique it can be solved for And
using suffix trees and fast algorithm LCA this problem can be solved .
However, the article described in this method is much simpler and has a lower latent constants
в asymptotics of time and memory. This algorithm was discovered Glenn Manakerom (Glenn Manacher)
в 1975
Trivial algorithm
To avoid ambiguities in the following description, we agree that there is a "trivial algorithm."
This algorithm, which is to find an answer at the position repeatedly tries to increase response by
one each time comparing a pair of corresponding symbols.
Such an algorithm is too slow, the whole answer as he may deem just in time
Algorithm Manakera
First learn to find all subpalindromes odd length, ie calculate the array; solution for palindromes of even length (ie,
Ie will consistently increase the value And check every time - true if the current
substring is a palindrome. When we find the first difference, or when we
we reach the borders line - stay we finally counted value . After that, we must
do not forget to update the
values .
However, there is subtletyWhich must be processed correctly when "internal palindrome" reaches
external borders or climbs over it, ie, (Or, equivalently,
). As for the external borders of the palindrome is no symmetry can not be guaranteed, then
simply assign is already correctly: we have enough information to say that in
position palindrome has the same length.
In fact, to properly handle these situations, it is necessary to "trim" subpalindromes length, ie assign. After that you
should let a trivial algorithm that will try to
(This illustration shows that, while the center of the palindrome at the position could be longer, beyond the outer limits of
the palindrome, - but in a position we can use only the part of it that actually fit into the outer palindrome. But the answer
to the position may be more than this part, so then we have to run a trivial search, which will try to push it beyond
external palindrome, ie to "try moving here".)
At the end of the description of the algorithm just happened to recall that we must not forget to update the values
after calculating the next value .
Also we should mention that we have described above reasoning to calculate the array of odd palindromes ; for
array even palindromes All the arguments are similar.
But a closer analysis shows that the algorithm is still linear. (It should refer to the known
algorithm for constructing the Z-line functions, internally which strongly resembles the algorithm, and also works
in linear time.)
In fact, it is easy to trace algorithm, each iteration produced a trivial finding leads to an increase of one border .
The decrease in in the course of the algorithm can not occur. Consequently, a trivial algorithm to make a sum
action.
Given that, except for trivial searches, all other parts of the algorithm Manakera obviously work
in linear time, we obtain the asymptotic behavior of the final: .
For the case subpalindromes odd length, ie for computing an array , We obtain the following code:
vector <int> D1
(n);int l =0, R = -
1;
for (int i =0; i <n; + + I) {
int k = (i>
r ?0 : Min (d1[l + r-i], R-i)) + 1;
while (i + k <n i-k> = 0 && S[i + k] == S[i-k]) +
&& + K;
d1[i] = K -;
if (i + k>
r)
l = i-k, r = i + k;
}
For subpalindromes even length, ie for computing an array Only slightly changed
arithmetic expressions:
vector <int> D2
(n); l =0, R = -1;
for (int i =0; i <n; + + I) {
int k = (i> r? 0 : Min (d2[l + r-i +1], R-i +1)) + 1;
while (i + k-1 <N && i-k> = 0 && S[i + k-1] == S[i-k]) +
+ K; d2[i] = - K;
if (i + k-1 > R)
l = i-k, r = i + k-1;
}
Duval algorithm
Duval algorithm (Duval's algorithm) finds a given string length Lyndon decomposition during
using additional memory. Will work with
strings in the 0-indexed.
We introduce the auxiliary notion predprostoy line. Line called predprostoyIf it has the form
Wherein - Some simple string and - Prefix of some .
Duval algorithm is greedy. At any time during the work string S is actually divided into three rows Where
line Lyndon decomposition has already been found and no longer used
algorithm; line - It predprostaya string (length and simple lines inside it, we also remember); line - It is not part
of the line treated . Every time algorithm Duval takes the first character and tries to add it to the string . At
the same time, perhaps, for some prefix string Lyndon decomposition becomes known, and this part of the
proceeds to line .
We now describe the algorithm formally. First, it will be supported by a pointer to the beginning of the line.
The outer loop of the algorithm is executed until Ie until the entire string does not go into a string. Inside
at the beginning
this cycle are two pointers: a pointer of the line (Actually a pointer to the next
symbol candidate) and a pointer the current character in the string with which to compare. Then we
in a loop trying to add character to line , Which must make the comparison with the symbol . Here
we're having three different cases:
● If , Then we can finish symbol to the line without breaking it "predprostoty." Consequently,
in this case we simply increment pointers and unit.
, It is obvious that the
● If line will be easy. Then we increase by one, and
we move back to To the next character compared with the first character.
● If , The string can no longer be predprostoy. So we split the string predprostuyu
on simple lines plus "balance" (a simple string prefix, possibly empty); add a simple string in response (ie, derive their
position, simultaneously moving a pointer), and the "balance" with the symbol translate back to
line And stop the execution of the inner loop. Thus we have the next iteration of the outer loop to
re-process residue, knowing that he could not form a line with previous predprostuyu simple
strings. It remains only to note that the derivation of simple lines of products we need to know their
length; but it is obviously .
Implementation
We present implementation of the algorithm Duval that will output the desired decomposition Lyndon line :
Asymptotics
Immediately, we note that the algorithm requires Duval Memory, namely three
pointer , , . We now estimate operation time algorithm.
While the outer loop makes no more iteration, since the end of each iteration, it displays at
least one symbol (total symbols are output rather obviously exactly ).
We now estimate the number of iterations first nested loop while. For this we consider
the second nested loop while - every time he's run a number of outputs copies of the
a simple string of some length . Note that the line predprostoy is, with its
simple strings have a length of just Ie its length does not exceed . Since the length of the string
equal And the pointer is increased by one at each iteration of the inner loop of the first while, then this
loop will execute no more iterations. The worst case is that And we get that
while the first nested loop executes every time no more iterations. Recalling that the total output
characters, we find that for output requires no more characters iterations of the first nested while-a.
This algorithm was proposed by Canadian scientists Alfred Aho (Alfred Vaino Aho) and
scientists Korasik Margaret (Margaret John Corasick) in 1975
struct vertex {
int next[K];
bool leaf;
};
vertex t[NMAX
+1];int sz;
Ie We will store boron as an array (number of elements in the array - it sz) structures .
Wherei
Structure contains a flag And an array of ribs n - Pointer to the top,
in which an edge on the symbol Or If no such edge.
Initially, boron consists of only one vertex - root (agree that the root is always in the array index ).
So initialization Boron is:
Now implement a function that will add boron given string . Implementation is very simple:
we get to the root of boron, see if there from the root of the transition in the
letter If the transition is, just go
on it in the other vertex, otherwise create a new vertex and add a transition to this vertex by letter . Then
we are standing in some vertex, repeat the process for the
letter Etc. After completing the mark
last visited the top of the flag .
Linear time work, as well as a linear number of vertices in boron obvious. Since each node have
memory, the memory usage has .
Memory consumption can be reduced to a linear (), but due to the increase to the asymptotic behavior of . It's
enough to keep the transitions not an array, and the mapping .
Construction machine
Suppose we have constructed a forest for a given set of strings. Look at it now, some on the other side. If we consider any
vertex, the line that corresponds to it, is a prefix of one or more rows from the set; ie each vertex of boron can be understood
as a position in one or more rows from the set.
In fact, the top of boron can be understood as the state deterministic finite
automaton. While in some state, we are under the influence of any input letters go to
a
n
another state - ie to another position in the rowset. For example, if boron is only string d
we are in the state (which corresponds to the line ), Then exposed to the letters we proceed in
condition.
Ie we can understand the edges of boron as transitions in the machine on the corresponding letter. However, only
some edges of boron can not be limited. If we try to make the transition to a letter, and the corresponding edge in
boron not, we still need to go to some state.
More precisely, let we are able to , Which corresponds to some string And want to jump on the symbol . If the
boron from the top there is a transition in the letter We just go along this edge and get to the top, which
corresponds to the line . If no such edge, then we must find a state corresponding to the line of the longest
proper suffix (Of the longest available in boron), and try to jump on the letter thereof.
For example, let Bor built in rows and And we exposed line moved to
a state that is a leaf. Then, under the influence of the letters we are forced to go to
state corresponding to the line And only from there jump to the letter .
Suffix link for each vertex - Is the pinnacle, which ends
of the longest proper suffix of the row corresponding to the top . The only special case - the root
boron; for the convenience of the suffix link from it to spend for themselves. Now we can reformulate the statement about
the transitions in the machine as follows: while the current top of the boron is no transition on the corresponding letter (or
until we come to the root of boron), we have to move on suffix link.
Thus, we have reduced the problem of constructing an automaton to the problem of finding suffix
links for all vertices of boron. However, to build these links, we will suffix, oddly enough, on the
contrary, with the help of built in automatic transitions.
Note that if we want to know the suffix link for some vertex , Then we can go to the ancestor
current node (let - A letter, of which at there is a transition in ), Then go to its suffix link, and
then jump out of it in the machine, according to the letter .
Thus, the problem of finding the transition is reduced to the problem of finding suffix links, and the task
of finding links suffix - suffix to the task of finding links and go, but for closer to the root vertices. We got
a recursive relationship, but not infinite, and, furthermore, that can solve in linear time.
We now turn to implementation. Note that we now need to store each vertex of its ancestor As well as the character to the
ancestor of a transition in our top. Also at each vertex will
store - Suffix link (or If it is not calculated), and an array - Transitions
in the machine for each of the characters (again, if the array element is equal , He has
not yet calculated). We now present the full realization of all necessary functions:
struct vertex {
int next[K];
bool leaf;
int p;
char pch;
int link;
int go[K];
};
vertex t[NMAX
+1];int sz;
void init() {
t[0]. P = t[0]. Link = -1;
memset (t[0]. Next, 255, sizeof t[0].
Next); memset (t[0]. Go, 255, sizeof t[0].
Go);
sz = 1;
}
It is easy to understand that, by remembering found suffix links and transitions, the total time for
finding all the suffix links and transitions is linear.
Applications
by suffix links. This value can be calculated for in the sum, and then for the current state
we can for find the number of occurrences of all the samples in the text, ending at the current position. Topics
Thus, the problem of finding the total number of occurrences can be solved for us .
such a graph, we will find the way to the state minimum length that we just needed.
The main function - She builds a suffix tree. Tree is stored as an array of structures ,
where - Root suffix tree.
For simplicity, code is stored in the edge of the same structures: for each node in its structure
recorded data on the edge that came out of her ancestor. Overall, each stored: Defining
label ribs ancestor - Top of ancestor - Suffix link - List
outgoing edges.
string s;
int n;
struct node {
int l, r, par, link;
map <char,int> Next;
node (int l =0,int r =0,int par = -1)
: L(l), R(r), Par(par), Link(-1) {}
int len() { return r - l; }
int & Get (char c) {
if (! Next.count(c)) next[c] = -1;
return next[c];
}
};
node t[MAXN];
int sz;
struct state {
int v, pos;
state (int v, int pos) : V(v), Pos(pos) {}
};
state ptr (0,0);
state go (state st, int l, int r) {
while (l <r)
if (st.pos == t[st.v]. Len()) {
st = state (t[st.v]. Get( s[l]),0);
if (st.v == -1) return st;
}
else {
if (s[ t[st.v]. L + st.pos ] ! =
S[l]) return state (-1-1);
if (r-l <t[st.v]. Len() - St.pos)
return state (st.v, st.pos + r-l);
l + = t[st.v]. Len() - St.pos;
st.pos = t[st.v]. Len();
}
return st;
}
void build_tree() {
sz = 1;
for (int i =0; i <n; + +
I) tree_extend
(i);
}
Compressed implementation
We also give the following compact realization algorithm Ukkonen proposed freopen:
node (int l = 0, int r = 0, int par = -1): l (l), r (r), par (par), link (-1) {} int len () {return r - l ; }
Int
Search all tandem repeats in a row.
Algorithm Maine-Lorentz
Given a string length .
Tandem repeats (Tandem repeat) it called two occurrences of a substring in a row. In other words, a pair of
tandem repeats is described indices such that substring - is
two identical rows recorded in a row.
The challenge is to find all tandem repeats. Simplified versions of this problem: find any tandem
repeat or find longest tandem repeat.
Note. To avoid confusion, all rows in the article, we will assume 0-indexed, ie the first character
has an index of 0.
Algorithm described here was published in 1982 and Maine Lorenz (see References).
Example
Consider the example of tandem repeats some simple string, for example:
Another example:
We present here some other interesting results related to the number of tandem repeats:
● It is known that if we consider only the primitive tandem repeats (ie, those halves which are not multiples of
rows), their number in any row - .
● If encode tandem repeats triples of numbers (called triples Krochemora (Crochemore))
(Wherein
- Starting position, - The length of the repeating substring - The number of repeats), all tandem repeats
Any string can be displayed with such triples. (This is the result obtained by
Krochemora output of the algorithm for finding all tandem repeats.) ●
The number of primitive tandem repeats in the Fibonacci lines - is also of order .
Algorithm Maine-Lorentz
Idea of the algorithm Maine-Lorentz is fairly standard: an algorithm "Divide-and-conquer".
Briefly it is that the original string is split in half, the solution starts from each of the two halves separately (thus we find all
tandem repeats, which are located only in the first or only in the second half). Next comes the hardest part - is finding
tandem repeats, beginning in the first half and the second ending (we call these tandem repeats
for convenience crossing). How it's done - and is the very essence of the algorithm Maine-Lorentz;
that we will describe below.
Asymptotics algorithm "divide-and-conquer" well researched. In particular, it is important for us that if we
, The final asymptotic behavior of
learn to look for crossing the tandem repeats in a string length for all
algorithm will .
Here and
through we denote the length of the two pieces of tandem repeat: - The length of the tandem repeat
An
to position d - The length of the tandem repeat of until the end of the halves of the tandem
Repeat. Thus, - The length of tandem repeat.
Looking at this picture, we can see that necessary and sufficient the condition that the center
at position is the length of the tandem repeat ,
is the following condition:
● Let - This is the largest number such that characters before your coincide with the latest
character string :
● All tandem repeats, which we now discover, will have a length . However, these tandem repeats
can be severalIt all depends on the choice of the lengths of the pieces and .
So the whole problem is reduced to the rapid calculation of the lengths and
for each value of . Recall their definitions:
● To quickly find the values pre-calculate Z-function string (Ie lines written out in the
reverse order).
Then the value for a particular is just equal to the corresponding value of the array Z-function.
● To quickly find the values pre-calculate Z-function string (Ie line
Attributed to the line through the separator character).
Again, the value of specific will have to just take the corresponding element of Z-function.
Asymptotics
Implementation
We present implementation of the algorithm Maine-Lorentz, during which finds all tandem repeats
this line in a compressed form (as a group described by four numbers).
In order to demonstrate the tandem repeats found during "Decompress" and displayed on
separately. This conclusion is in solving real-world problems will be easily replaced by some
other, more effective actions, for example, search of the longest tandem repeat, or count the
number of tandem repeats.
void output_tandem (const string & s, int shift, bool left, int cntr, int
l, int l1, int l2) {
int pos;
if (left)
pos = cntr-l1;
else
pos = cntr-l1-l2-l1 +1;
cout << "[" << Shift + pos << ".." << Shift + pos +2* L-1 << "] ="
<< s.substr (pos, 2* L) << Endl;
}
void output_tandems (const string & s, int shift, bool left, int cntr, int
l, int k1, int k2) {
for (int l1 =1; l1 <= l; + + L1) {
if (left && l1 == l) break;
if (l1 <= k1 && l-l1 <= k2)
output_tandem (s, shift, left, cntr, l, l1, l-l1);
}
}
inline int get_z (const vector <int> & Z, int i) {
return 0<= I && i <(int)z.size() ? z[i] :
0;
}
Literature
● Michael Main, Richard J. Lorentz. An O (n log n) Algorithm for Finding All Repetitions in a
String [1982]
● Bill Smyth. Computing Patterns in Strings [2003]
● Bill Smith. Methods and algorithms for computing row [2006]
Search substring in a string using a Z-or-
prefix function
Two strings S and T. required to find all occurrences of the string in the text of S T for O (N), where N - the total length of
strings S and T.
Algorithm
Form the string S $ TWhere $ - a separator which is not found in S, or T. in
We compute for the resulting string prefix function P for O (N). Walk through the array P, and consider all the
elements that are equal to | S | (length S). By definition prefix certain functions, which means that in this place
ending substring that matched | S |, ie the desired entry. Thus, we find all occurrences. This algorithm is called an
algorithm ILC (Knuth-Morris-Pratt).
Now solve the same problem using Z-function. Construct in O (N) array Z - Z-line function S $ T. Go through
its elements, and consider those that are equal to | S |. By definition, in this place starts the substring that
matched S. Thus, we find all occurrences.
Solution of the problem "row compression" for O (N)
S. A string is required to find such a string T, the string S is obtained by rote T. Of all the possible
T to choose the smallest length.
This task is very simple to solve for O (N) using the prefix function.
So, let the array P - prefix-row function S, which can be computed in O (N).
Now consider the last element P: P [N-1]. If N is divisible by (N - P [N-1]), then the answer is there, and it is N - P
[N-1] the first letters of the string S. If not divisible, then there is no answer.
The correctness of this method is easy to understand. P [N-1] is equal to the length of the longest line of its own suffix
S, coincides with the prefix S. If there is a response, it is evident that the initial bit string S length (N - P [N-1]) is the
response, and therefore, N is divided into (N - P [N-1 ]). If there is no answer, the (N - P [N-1]) is equal to some inexplicable
value to which N will not share (otherwise the answer existed).
Implementation
Description
The basic idea sqrt-decomposition is that we will do next predposchet: Divide the into blocks of length approximately
Although the last block may be less than , Components (if not divided into ) - It does not matter.
So, let these values previously counted (to do this, obviously, operations). That they can
to the calculation of response to another request ? Note that if the segment long, then it
will contain several blocks entirely, and these blocks we can find out the amount to them in a single operation. As a result,
the total length of two blocks will only entering it only partially, and these lumps us
will produce a trivial summation algorithm.
Illustration (here denotes block number, which is And through - Number of a block in which is )
This figure shows that in order to calculate the amount of segment , It is necessary to sum items only
two "tails": and And sum the values in all blocks, starting
with and ending :
(Note: this formula is incorrect when: in this case, some items are summed twice, in which case you just
have to sum elements by )
Thus we have a significant amount of ekononim operations. Indeed, the size of each of the "tails" are obviously not exceed the length
of the block And also the number of blocks does not exceed . Since we chose Then
to calculate the sum of all the segment we need only operations.
Implementation
We first present a simple implementation:
// input int n;
vector <int> A
(n);
// predposchet
int len = (int) sqrt (n + .0) + 1; / / and a block size and the number of
blocks vector <int> B (len);
for (int i =0; i <n; + + I)
b[i / len] + =
A[i];
/ / Response to
requests for (,;) {
int l, r; / / reads input - another request int sum = 0;
for (int i = l; i <= r; )
if (i% len == 0 && I + len - 1 <= R) {
/ / If i indicates the beginning of the block lying entirely
in [l; r]
sum + = b[i /
len]; i + = len;
}
else {
sum + =
a[i]; + + I;
}
}
The disadvantage of this implementation is that it unreasonably long division operations (which are known to run
significantly slower than the other operations). Instead, you can count the number of blocks and , Which lie in the
boundary and respectively, and then make a loop with the blocks on,
separately processed "tails" in the blocks and . Furthermore, in case of this implementation
becomes specific and requires a separate processing:
int sum = 0;
int c_l = l / len, c_r = r / len;
if (c_l == c_r)
for (int i = l; i <= r;
+ + I) sum + =
a[i];
else {
for (int i = l, end =(c_l +1)* Len-1; i <=
end; + + I) sum + = a[i];
for (int i = c_l +1; i <= c_r-1;
+ + I) sum + = b[i];
for (int i = c_r * len; i <=
r; + + I) sum + =
a[i];
}
Other problems
We have examined the problem of finding the sum of array elements in some of its subsegments. This task can
expand a bit: also solve change individual array elements. Indeed, if
changing an element, it is sufficient to update the value of in the block in which this element
is ( )
On the other hand, instead of the amount of similar problems can be solved the problem of minimum, maximum
elements in the interval. If these problems to allow changes of individual elements, that too will need to recalculate
the value of the unit, which owns variable element, but recalculate already fully pass on all elements of the unit for
operations.
Similarly sqrt-decomposition can be applied to the set other similar problems:
finding the number of zero elements, the first non-zero element, counting the number of certain
elements, etc.
There are a class of problems occur when change elements on the whole subsegments: addition
or appropriation of elements of the array at some subsegments .
For example, you must perform the following two types of queries added to all elements of a certain length
value, and recognizing the value of an individual item . Then, as Put that amount
which must be added to all members of the second block (for example, initially all ); then
when the query is "addition" will need to perform the addition of all elements "Tails", and
. And the
then perform the addition of all elements for blocks lying entirely in the interval answer
the second database is likely to be a Wherein . Thus, adding the interval
will be performed for And the request is a separate element - for .
Finally, you can use both types of tasks: changing elements on the interval and response to requests for the same interval. Both
types of operations will be carried out for. To do this already will have to do two "block" array and :
one - to secure changes in the interval, the other - to answer queries.
Can give an example, and other tasks to which you can apply sqrt-decomposition. For example, we can solve the problem of
maintaining a set of numbers with the ability to add / remove numbers, check the numbers on the accessory set, search th
order number. To solve this problem it is necessary to store numbers in sorted order, separated by a few blocks of numbers
in each. When adding or
removing the need to produce a number of "rebalancing" blocks, throwing the number of start / end of
some units in the beginning / end of the neighboring blocks.
it should be fast enough - during or a little worse; denote this time via .
Made in the following manner. At the beginning of each block queries see which edges in this block will be removed and immediately
remove Count them out. Now we construct a system of disjoint sets (dsu) on the resulting graph.
As we now have to respond to another request from the current block? Our system of disjoint sets "knows" all the edges,
except those that are added / removed in the current block. However, removal of dsu we do not need - we have removed
all such advance edges of the graph. Thus, all that may be - is more, add the ribs, which can be a maximum pieces.
Therefore, in response to the current request requesting we can just keep the bypass wide at the connected
components dsu, that will work for, as we are only in the consideration
Ribs.
pair: . Ie we sorted requests to the number sqrt-block in which lies the left end and
being equal - on the right end.
Consider now the group queries with the same value and see how we can handle
it. All such requests we ordered at the right margin, which means that we can just start with a blank portion And
repeatedly incrementing the right border - in the end to answer all these questions.
Good example for this heuristic is such a task: find the number of different numbers in the segment array
. This problem is difficult to solve by classical methods.
Slightly more complicated version of this problem is problem with one of the rounds Codeforces.
Fenwick tree
Fenwick tree - a data structure, tree on the array, having the following properties:
1) allows to calculate the value of a reversible operation G on any interval [L; R] during O (log N);
Description
For ease of description, we assume that the operation of G, on which we build the tree - it amount.
Suppose we are given an array A [0 .. N-1]. Wood Fenwick - array T[0 .. N-1], in which each
element is stored programming of certain elements of the array A:
Ti = sum Aj all F (i) <= j <= i,
The sum function works as follows. Instead of going over all the elements of the array A, it moves
the array of T, making the "jumping" through the segments where possible. First, it adds to the response value of the sum on
the interval [F (R); R], then takes the sum on the interval [F (F (R) -1); F (R) -1], and so on, until it reaches zero.
Function inc moves in the opposite direction - in the direction of increasing indices, updating sum value Tj
only for those products for which it is necessary, ie for all j, for which F (j) <= i <= j.
Obviously, the choice of the function F is dependent on both the speed of the operations. Now we consider the
function that will achieve logarithmic performance in both cases.
Determine the value of F (X) follows. Consider the binary representation of the number and look at the its least significant
bit. If it is zero, then F (X) = X. Otherwise, the binary representation of X ends with group
one or more units. Replace all the units of the group for the zeros, and assign the resulting
number value of the function F (X).
This corresponds to a rather complicated description of a very simple formula:
We just need to learn how to quickly find such number j, for which F (j) <= i <= j.
However, it is not difficult to make sure that all such numbers are obtained from i j successive replacements of the
right (the youngest) zero in the binary representation. For example, for i = 10, we find that j = 11, 15, 31, 63, etc. Oddly enough, this
operation (replacement youngest zero to one) also corresponds to a very simple formula:
H (X) = X | (X +1),
vector <int>
t; int n;
const int INF = 1000 * 1000 * 1000;
more ) - And at the time averaging the request (see "Storage DSU as an explicit list sets").
Naive implementation
We can already write the first implementation of a system of disjoint sets. It will be quite inefficient, but then we will improve it with two
receptions, receiving a result of almost constant work.
So, all the information about the sets of elements stored with us using the array .
To create a new item (operation ), We simply create a tree rooted at the top,
Noting that her father - it herself.
To combine the two sets (operation ), We first find the set of leaders in
which is And set in which is located. If leaders coincide, then do not do anything - it means that the sets
already been merged. Otherwise, you can simply indicate that the ancestor of a vertex is (Or vice versa) -
thereby attaching one tree to another.
Finally, the implementation of the search operation leader ( ) Is simple: we ascend from the top of the ancestors ,
until we reach the root, ie while the reference to the ancestor is not a. This operation is easier to implement
recursively (especially it will be convenient later, in connection with optimizations being added).
However, such an implementation of disjoint sets very ineffective. It is easy to construct an example, when, after
several unions of sets get a situation that many - this tree, degenerated into a long chain. As a result, each call will work
in this assay
This simple implementation does all that meant: first, by the recursive call is the leader of the
set, and then, during stack unwinding, this leader is assigned to all the links traversed
elements.
Implement this operation and can be non-recursive, but then have to perform two passes on a tree:
first find the desired leader, the second - it would put all the vertices of the path. However, in practice,
a non-recursive implementation of no significant payoff.
We show that the asymptotic behavior of the system of disjoint sets using only rank
heuristics, heuristics without compression paths will logarithmic per request, on average: .
Here we show that for any of the two options rank heuristics depth of each tree will be
value That will automatically mean logarithmic asymptotics for the request ,
and therefore, the request .
Consider the rank heuristic depth tree. We show that if the rank of the tree is then
The tree contains at least vertices (hence will automatically follow that the rank and, hence,
it's obvious. When
depth of the tree is the value ). Will prove by induction for compressing
ways depth can only decrease. Rank increases with tree to When it is joined
tree rank ; applying to these two trees size induction hypothesis, we get that
new tree rank really will have at least vertices, as required.
We now consider the rank heuristic size trees. We show that if the size of
Wood is, its height is not more . Will prove by induction for statement is true.
When compressing ways depth can only decrease, so path compression does not violate anything. Let
now combined two tree sizes and; then by the induction of their height is less than or
are, respectively, and . Without loss of generality, we assume that the first tree - more
( ), So after the unification of the depth of the resulting tree vertices will be equal:
void init() {
for (int i =0; i <L; + + I)
make_set (i);
}
void process_query (int l, int r, int c) {
for (int v = l; ; ) {
v = find_set (v);
if (v> = r) break;
answer[v] = C;
parent[v] = V +1;
}
}
However, this solution can be implemented with rank heuristics: We keep for each set from an array, where the
set ends (ie, the right-most point). Then it will be
combine two sets to one in their rank heuristics, then affixing received many new right border. Thus, we
obtain a solution for .
to the edges in the tree from the current node to the root of the tree).
If there were no compression heuristic ways, then there are no problems would not arise - the distance
just to the root would be equal to the number of recursive calls that did function .
However, as a result of compression paths several edges of the path could be compressed into a single
edge. Thus, with each vertex must store additional information: length of the current edge from vertex
in the ancestor.
When implementation is convenient to represent the array and function now return more than one number,
а a pair of numbers: the top leader and the distance to it:
Thus, no matter how many joins what, you should use the specified formula to set the parity edges conducted
from one leader to another.
We present implementation DSU supporting parities. As in the previous paragraph, for purposes of
convenience, we use a pair of storage ancestors and results of operations. In addition, for each set we
store in the array Whether it is still bipartite or not.
if (a == b) {
if (x == y)
bipartite[a] = false;
}
else {
if (rank[a] <Rank[b])
swap (a, b);
parent[b] = Make_pair (a, x ^ y ^
1);if (rank[a] == Rank[b])
+ + Rank[a];
}
}
number And determine what response to a request, this number should be. To do this, we need to find
this number - it is easy to understand that this
first unanswered request going after adding is
that request, the response to which is a number.
Thus, there is obtained an idea similar to problem of painting segments.
Can obtain a solution for an average of inquiry, if we abandon the rank and heuristics will
easy to store in each element closest link to the right query And use compression
way to maintain these links after associations.
It is also possible to obtain a solution
and If we use the rank heuristic and will store
in each set position number where it ends (that in the previous version of the solution is achieved automatically by the fact
that the links were always just right - now we have to be stored explicitly).
Algorithm for finding the LCA (lowest common ancestor in the tree) for
average offline
Tarjan's algorithm for finding the LCA average online described in corresponding article.
This algorithm compares favorably with other search algorithms LCA in its simplicity (especially
when compared to optimal algorithm Farah-Colton-Bender).
well as update the leader of all the elements of one of the two lists.
However, as it turns out, the use of weight heuristicsSimilar to that described above, can significantly reduce the
Under the weight heuristic means that we are always will add the lesser of
two sets in more. Addition one set to another is easy to implement
during the order of the size of the set to add and search leader - During when such
storage method.
We prove asymptotics to perform queries. Fix an arbitrary element
and examine how it affected the operations of union . When an element
worked first time, we can say that the size of its new set will be at least . When
exposed a second time - it can be argued that it reaches the set size no less (As we add more to the smaller
set). And so on - we see that an element could affect the maximum merge operations. Thus, the sum over all
vertices of this
vector <int>
Lst[MAXN];int
parent[MAXN];
void make_set (int v) {
lst[v] = Vector <int>(1, V);
parent[v] = V;
}
Also, the idea of adding elements to a smaller set can be used more and outside the DSU, to
solve other problems.
For example, consider the following task: Given a tree, each leaf is attributed to any number (the same
number may occur several times in different leaves). Required for each vertex see the number of different
numbers in its subtree.
Applying in this problem the same idea, you can get a solution: Let traversal depth Wood, who
will return a pointer to properties - a list of all the numbers in the subtree of this vertex. Then, to get an answer to the
current node (unless, of course, she did not list) - bypassing the need to call in the depth of all children of this vertex, and
combine all received One, the size of which will be the answer for the current node. To effectively combine multiple
One just apply the techniques described above: will combine two sets, simply by adding one element in a larger set of
smaller. As a result, we obtain a solution
On the other hand, in practical applications often need to learn how to connect two wood specified edge is not
necessarily coming out of their roots. This means that we have no other choice but to perepodvesit one of the trees
for the specified vertex then we were able to attach a tree to another, making the root of the tree of child nodes to be
added to the second end edge.
At first glance it seems that the operation perepodveshivaniya - very costly and greatly worsen
asymptotics. Indeed, in the tree top perepodveshivaniya we must go from this vertex to
root of the tree, updating all pointers and .
However, in reality things are not so bad: you simply perepodveshivat from the two trees, which is less
than one to get the asymptotic behavior of the association, equal avg.
More details (including proof of the asymptotics) see ability to bridge in the graph for average
online.
Historical Retrospective
The data structure "system of disjoint sets" was known relatively long time.
A method of storage of this structure in the form of forest trees was apparently first described by
Haller and Fisher in 1964 (Galler, Fisher "An Improved Equivalence Algorithm"), but a full analysis
of the asymptotic behavior was carried out much later.
Heuristics compression paths and associations ranked, seem developed McIlroy (McIlroy) and Morris
(Morris), and independently from them, Tritter (Tritter).
Some time was known only estimates One operation in the middle, this Hopcroft
and Ullman in 1973 (Hopcroft, Ullman "Set-merging algomthms") - here -iterated
logarithm (It is slowly increasing function, but not so slow as the inverse Ackermann function).
Literature
● Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein. Algorithms: Construction
and analysis [2005]
● Kurt Mehlhorn, Peter Sanders. Algorithms and Data Structures: The Basic Toolbox [2008]
● Robert Endre Tarjan. Efficiency of a Good But Not Linear Set Union Algorithm [1975]
● Robert Endre Tarjan, Jan van Leeuwen. Worst-Case Analysis of Set Union Algorithms [1985]
Segment tree
Segment tree - a data structure that allows efficient (ie the asymptotic behavior )
implement the following operations: finding the amount / minimum of array elements in a given
interval ( Where input to the algorithm), the method further may change
array elements: how changing the value of one element, and change elements on a whole array of subsegments (ie, all
the elements are allowed to assign a value, or to add to all the elements
array any number).
Generally, the segment tree - a very flexible structure and the number of problems solved her theoretically unlimited. In
addition to the above types of transactions with trees segments are also possible and much more complex operations (see
"Complicated version tree segments"). In particular, the segment tree is easily generalized to higher dimensions: for example,
to solve the problem of finding the amount / minimum in some
again, the segment tree must handle both the request for time .
Construction
The process of constructing the tree segments for a given array can be done efficiently as follows, from the bottom up: first,
record the values of the elements in the corresponding leaves of the tree, then on the basis of
They calculate values for the vertices of the previous level as the sum of the two leaves, and then a similar manner
to calculate the value of another level, etc. Convenient to describe this operation recursively: we run the procedure
of construction of the root segments, and the procedure of construction, if it is not caused by the sheet, calls itself
from each of two sons and summarizes the calculated values, and if it is caused by the sheet - simply writes a value
of the array element.
Asymptotics tree construction segments will thus .
Request amount
We now consider the amount of the request. Are input two numbers and And we have time for
consider
Update request
Recall that the update request receives the input index and the value of And rearranges the tree segments so as to
conform to the new value. This request must also be performed for
time .
It is more simple compared to the query request amount counting. The fact that the element involves only
a relatively small number of vertices of the segments: namely, in tops - one on
each level.
Then it is clear that the update request can be implemented as a recursive function: it is passed the current
node of the tree lines, and this function performs a recursive call from one of his two sons (from that which
contains the position in its segment), and after that - recalculates the value of the amount of the current
vertex in the same way as we did in the construction of the tree segments (ie, the sum of the values for both
sons of current node).
Implementation
Realizable main point - is that, as store wood segments in memory. For simplicity, we will not store a tree in an explicit form,
and use this trick: we say that the root of the tree has a number ,
his sons - the rooms and their sons - rooms on and so forth. Easy to understand the correctness of the following
formula: if the node has room , Then let her son left - is the pinnacle of a number And the
right - with the number .
This technique greatly simplifies the programming tree segments - now we do not need to be stored in the memory tree structure
of segments, but only to have a array of amounts on each segment of the tree segments.
One has only to note that the size of the array at a numbering should not put And . The fact that this numbering does
not work perfectly when not a power of two - then there are missed calls, which do not correspond to any vertex of the
tree (actually, numbering behaves just as if be rounded up to the nearest power of two). This does not pose any
difficulties in the implementation, however leads to the fact that the size of the array must be increased to .
So, we segment tree store just as an array , Four times larger than the size Input:
Procedure tree construction segments for a given array is as follows: a recursive function, passing it the array, the
number the current top of the tree, and the boundaries and segment corresponding to the current top of the
tree. From the main program should call this function
с parameters , , .
Further, the function for request amount also is a recursive function in the same way that information is transmitted to the
current top of the tree (ie, the number ,,, Which in the main program should pass , Respectively), and in addition to this -
and also borders current request. In order to simplify this code fukntsii always makes two recursive calls, even if really need
one
- Just extra recursive call request be passed, which it is easy to cut off
additional check in the beginning of the function.
Finally, modification request. He just passed the information about the current top of the tree segments, and
further, the index changing element, as well as its new value.
void update (int v, int tl, int tr, int pos, int new_val) {
if (tl == tr)
t[v] = New_val;
else {
int tm = (tl + tr) / 2;
if (pos <= tm)
update (v *2, Tl, tm, Pos, new_val);
else
update (v *2+1,tm+1, Tr, pos, new_val);
t[v] = T[v *2] + T[v *2+1];
}
}
It is worth mentioning that the function of easy to make a non-recursive because it tail recursion, so
is branching never happens: one call can produce only one recursive call. When non-recursive
implementation, speed can increase by several times.
From other optimizations worth mentioning is that multiplication and division by two is necessary to replace bit operations
- It is also slightly improves the performance of the tree segments.
Minimum / maximum
Little to change the conditions of the problem described above: instead of requesting a sum will
produce now request the minimum / maximum on the interval.
Then the tree segments for this practically does not differ from the tree segments described above. Just
need to change the method of calculating the functions and as well as calculated
returned response function (Replace the summation by minimum / maximum).
Combining two such pairs in the same worth as a separate function because this operation will need to
produce and modify the query, and the query search for the maximum.
pair <int,int> Get_max (int v, int tl, int tr, int l, int r)
{ if (l> r)
return make_pair (-INF, 0);
if (l == tl && r == tr)
return t[v];
int tm = (tl + tr) / 2;
return combine (
get_max (v *2, Tl, tm, L, min(r,tm)),
Get_max (v *2+1,tm+1, Tr, max(l,tm+1), R)
);
}
void update (int v, int tl, int tr, int pos, int new_val) {
if (tl == tr)
t[v] = Make_pair (new_val, 1);
else {
int tm = (tl + tr) / 2;
if (pos <= tm)
update (v *2, Tl, tm, Pos, new_val);
else
update (v *2+1,tm+1, Tr, pos, new_val);
t[v] = Combine (t[v *2], T[v *2+1]);
}
}
Still given to the input array And receives requests Which means: find
And the amount of this
a subsegment That , segment maximum.
Requests modification of individual elements of the array are allowed. Array elements can be negative (and, for example, if all the
numbers are negative, the optimal subsegments will be empty - it sum is zero).
This is a very nontrivial generalization tree segments obtained as follows. Will be stored in each node of the tree segments
four values: the amount in this segment, the maximum amount of all
prefixes of this segment, the maximum amount of all suffixes, as well as the maximum amount for subsegments
it. In other words, for each segment of the tree segments the answer to it already predposchitan and
additionally answer counted among all the segments that are limited in the left margin of the segment, as
well as among all the segments that are limited in the right border.
How do you build tree to such data? Again we come to this point of view with recursive let for the current
top four values in the left son and son in law already counted, count them now for the summit. Note that
the answer is in the very top:
● or response in the left son, which means that the best in the current top subsegment can actually fit
into a segment of the left son
● or answer in the right son, that means that the best in the current top subsegment can actually
fit into a segment of the right son
● or maximum amount of the suffix in the left son and the maximum prefix in the right son, that means that
the best subsegment is its origin in the left son, and the end - in the right.
Hence, in response to the current vertex is the maximum of these three values. Recalculate the
maximum amount on the same prefix and suffix even easier. We present the implementation of
functions, which will be passed two structures, inclusive of the left and right of the sons, and which
returns the data in the current vertex.
struct data {
int sum, pref, suff, ans;
};
So we learned how to build a tree segments. Is easy to obtain and implement modification request: as in the simple tree
segments, we recalculate the values in the tree tops all changed
segments, which all use the same function . To calculate the values of tree leaves
and the auxiliary function That returns the structure Calculated one
numbe
r .
void update (int v, int tl, int tr, int pos, int new_val) {
if (tl == tr)
t[v] = Make_data (new_val);
else {
int tm = (tl + tr) / 2;
if (pos <= tm)
update (v *2, Tl, tm, Pos, new_val);
else
update (v *2+1,tm+1, Tr, pos, new_val);
t[v] = Combine (t[v *2], T[v *2+1]);
}
}
It remains to deal with the response to the request. To do this, we have also, as before, down the tree, breaking those
most segment request several subsegments, coincides with the segment tree segments and
They combine the responses into a single response for the whole task. Then it is clear that the work is no
different from ordinary wood work segments, instead of just need a simple summation / minimum / maximum
values use the . The following implementation is slightly different from the sale of
Request : It does not allow for cases when the left boundary request exceeds the right boundary (otherwise
any unpleasant incidents - what structure I return when the query interval is empty? ..).
The simplest version of the application of this technique - where each node of the tree segments stored sorted
list of all numbers appearing in the corresponding interval. In more complex scenarios are not stored lists, and
any data structure built on these lists ( , etc.). But all these methods have in common is that each node
of the tree segments stored some data structure in memory having a size of about the length of the
corresponding segment.
The first natural question is posed when considering trees segments of this class - is memory usage. Allegedly
that if each node of the tree segments stored
a list of all occurring at this interval number or any other data structure size of the same order, the sum of all segment tree
will occupy memory cells. Why is this so? Because each number
flagged tree segments segments (not least because the height of the tree segments have ).
Thus, despite the apparent extravagance of the tree lines, it consumes memory is not much
longer than normal wood segments.
Below described are some typical uses such a data structure. It is worth noting immediately clear analogy
trees segments of this type two-dimensional data structures (Actually, in a sense, this is a two-dimensional
data structure, but rather with disabilities).
Find the smallest number greater than or equal to the specified in this interval.
No modification requests
Required to respond to requests from the following: That is to find the minimum number in the interval
Thus, in response to a request occurs subsegments , And the entire request is being processed for
time .
int query (int v, int tl, int tr, int l, int r, int x) {
if (l> r)
return INF;
if (l == tl && tr == r) {
vector <int> :: Iterator pos = lower_bound (t[v]. Begin(),
T[v].
end(), X);
if (pos! = t[v].
End()) return
* Pos;
return INF;
}
int tm = (tl + tr) / 2;
return min (
query (v *2, Tl, tm, L, min(r,tm), X),
Query (v *2+1,tm+1, Tr, max(l,tm+1), R, x)
);
}
Constant is equal to some large number, certainly more than any number in the array. It
carries the meaning of "response in a given interval does not exist."
Find the smallest number greater than or equal to the specified in this interval.
Allowed modification requests
Problem is similar to the previous one, only now allowed modification requests: assignment process .
The solution is also similar to the preceding problem, but instead of a simple list in each node of the tree
segments we store a balanced list that allows you to quickly search for the desired number, remove it and insert a
new number. Given that the general number of the input array can be repeated, the best choice is the structure of
STL data .
Construction segments of the tree occurs in about the same as in the previous problem, just now
That will lead to the fact that construction of the
should unite not sorted lists, asymptotics
deteriorate to (Although, apparently, red-black trees allow to merge two trees
in linear time, but the STL does not guarantee it).
The answer to search request general practically equivalent to the code above, only
now need to call on .
Finally, modification request. To handle it, we have to go down the tree, making changes to all lists containing
affect items. We simply remove the old value of this element
(Do not forget that we do not need to remove them all together with the number of repetitions) and insert the new value.
void update (int v, int tl, int tr, int pos, int new_val) {
t[v]. Erase (t[v]. Find (a[pos]));
t[v]. Insert (new_val);
if (tl! = tr) {
int tm = (tl + tr) / 2;
if (pos <= tm)
update (v *2, Tl, tm, Pos, new_val);
else
update (v *2+1,tm+1, Tr, pos, new_val);
}
else
a[pos] = New_val;
}
Find the smallest number greater than or equal to the specified in this interval.
Acceleration using the technique of "partial cascading"
Improve response time to find time by applying art
"Partial cascading" ("Fractional cascading").
Partial cascading - is a simple technique that helps to improve the work of several binary searches being conducted by
the same value. In fact, the response to the search request is that we divide our task into several subtasks, each of which
is then solved for the number of binary search . Partial cascading allows to replace all of these binary searches on one.
The simplest and most obvious example is the partial cascading the next challenge: There are
several sorted lists of numbers, and we have in each list to find the first number is greater than or
equal to the specified value.
If we solved the problem of "head", it would have to run a binary search on each of these lists,
, Then the
What if a lot of these lists, it becomes a very important factor: if all lists asymptotic behavior
will Where - the total size of all lists (such asymptotics, because the worst
in case - where all of the lists are approximately equal in length to each other, i.e. equal ).
Instead, we could combine all these lists into one sorted list, where each number will keep a list of positions: first position in
the list of the first number is greater than or equal,
a similar position in the second list, and so on. In other words, for each number we keep occurring at the same time the
number of binary search results on it in each of the lists. In this case,
asymptotic behavior of the response to the
request is received , Which is significantly better, but we
forced to pay a large memory consumption: namely, we need memory cells.
Tech partial cascading going on in this task and achieves memory consumption at
the same time the answer to the inquiry . (To do this, we keep not one big list length ,
and back again to the lists, but with each list is stored every second element from the following list; we again
have the number with each record its position in both lists (current and next), but it will continue to effectively
respond to a request: we do a binary search on the first list, and then go on these lists in order, moving each
time The following list of predposchitannyh using pointers and taking one step to the left, thereby considering
that half of the next list number has not been taken into account).
But we in our application to the tree segments not needed full power of this technology. The fact that the
list contains the current node to all the numbers which can occur in the left and right sons. Therefore, to
avoid a binary search through the list of his son, it is sufficient for each list in the tree segments for each
count of the number of its position in the list of the left and right sons (more precisely, the position of the
first number is less than or equal to the current).
Thus, instead of a simple list of all the numbers we store a list of triples: the number itself, the position in the list left child,
the right position in the list of his son. This will allow us to determine the position in the list left or right
son, instead of doing a binary list on it.
The easiest way to apply this technique to the problem when no modification requests - then these
positions are just numbers, and count them in the construction of the tree very easily inside the
algorithm merge two sorted sequences.
In the case of modification requests are allowed, all a bit more complicated: these positions now be
stored in the form of iterators inside, and when you request an update - the right to reduce / increase
for those elements for which it is required.
Anyway, the problem is reduced to net realizable subtleties, but the basic idea - replacing a binary search binary
Other possible
Note that this technique implies a whole class of possible applications - everything is determined by the structure of the data
selected for storage in each node of the tree. We have examined the application
using and , While in general you can use any other
compact data structure: other tree segments (about this little discussed below in the section on
multivariate trees segments) Fenwick tree,Cartesian tree etc.
void update (int v, int tl, int tr, int l, int r, int add) {
if (l> r)
return;
if (l == tl && tr == r)
t[v] + = Add;
else {
int tm = (tl + tr) / 2;
update (v *2, Tl, tm, L, min(r,tm), Add);
update (v *2+1,tm+1, Tr, max(l,tm+1), R, add);
}
}
behavior ).
When implemented, this means that we need to make a function that will be transferred to the vertex
tree lines, and it will produce pushing information from this vertex in both her sons. Should call this
function at the beginning of request processing functions (but not call it from the leaves, because of the
push plate information is not necessary, and nowhere).
void update (int v, int tl, int tr, int l, int r, int color) {
if (l> r)
return;
if (l == tl && tr == r)
t[v] = Color;
else {
push (v);
int tm = (tl + tr) / 2;
update (v *2, Tl, tm, L, min(r,tm), Color);
update (v *2+1,tm+1, Tr, max(l,tm+1), R, color);
}
}
Other Destinations
Here were considered only basic application segments trees in problems with the modifications on the
segment. Other objects are obtained based on the same ideas as described herein.
It is only important to be very careful when dealing with pending modifications: it should be remembered that even if the current
vertex we have "pushed" the pending revision, the left and right sons likely
have not yet done. Therefore it is often necessary to call is also on the left and right sons
the current node, or as carefully consider pending modifications in them.
So, we will build a two-dimensional tree segments: first segment tree in the first coordinate ( ),
Then - on the second ( ).
That the process of constructing more understandable, it is possible to forget that the original array was two-dimensional,
and leave only the first coordinate. We will construct the usual one-dimensional segment tree, working only with
first coordinate. But as the value of each segment, we will not record a number, as in
dimensional case, and the whole tree segments: ie at this point we are reminded that we still have the second and
coordinate; but since at this point is recorded that the first coordinate is an interval Then
we actually work with the band And for her to build a tree segments.
We present the implementation of operations for constructing a two-dimensional tree. It actually consists of two
separate units: the construction of the tree segments coordinate () And the coordinate (). If
The first function is almost indistinguishable from the conventional one-dimensional tree, the latter is forced to deal
separately two cases: when the current segment in the first coordinate ( ) Has unit length, and
And the
when - length greater than one. In the first case, we simply take the required value from the matrix second
- Combine the values of two tree lengths of the left and right son son coordinate .
void build_y (int vx, int lx, int rx, int vy, int ly, int ry) {
if (ly == ry)
if (lx == rx)
t[vx] [vy] = A[lx] [ly];
else
t[vx] [vy] = T[vx *2] [vy] + T[vx *2+1] [vy];
else {
int my = (ly + ry) / 2;
build_y (vx, lx, rx, vy *2, Ly, my);
build_y (vx, lx, rx, vy *2+1, My +1,
Ry); t[vx] [vy] = T[vx] [vy *2] + T[vx]
[vy *2+1];
}
}
int sum_y (int vx, int vy, int tly, int try_, int ly, int ry) {
if (ly> ry)
return 0;
if (ly == tly && try_ == ry)
return t[vx] [vy];
int tmy = (tly + try_) / 2;
return sum_y (vx, vy *2, Tly, tmy, ly, min(ry, tmy))
+ Sum_y (vx, vy *2+1, Tmy +1, Try_, max(ly, tmy +1), Ry);
}
int sum_x (int vx, int tlx, int trx, int lx, int rx, int ly, int ry) {
if (lx> rx)
return 0;
if (lx == tlx && trx == rx)
return sum_y (vx, 1, 0, M-1, Ly, ry);
int tmx = (tlx + trx) / 2;
return sum_x (vx *2, Tlx, tmx, lx, min(rx, tmx), Ly, ry)
+ Sum_x (vx *2+1, Tmx +1, Trx, max(lx, tmx +1), Rx, ly, ry);
}
This function works in the time since it first down the tree in the first coordinate, and for each vertex passed this tree -
makes a request for a normal tree segments to the second coordinate.
Finally, we consider modification request. We want to learn how to modify the segment tree
in accordance with a change in the value of an element . It is clear that changes will occur only
in those segments of the first tree tops that cover coordinate (And there will be ), And for
tree segments, corresponding to it - changes are only those vertices that cover coordinate (And there is).
Therefore, the implementation of the modification request will not differ greatly from
dimensional case, but now we first down in the first coordinate, and then - on the second.
void update_y (int vx, int lx, int rx, int vy, int ly, int ry, int x, int
y, int new_val) {
if (ly == ry) {
if (lx == rx)
t[vx] [vy] = New_val;
else
t[vx] [vy] = T[vx *2] [vy] + T[vx *2+1] [vy];
}
else {
int my = (ly + ry) / 2;
if (y <= my)
update_y (vx, lx, rx, vy *2, Ly, my, x, y, new_val);
else
update_y (vx, lx, rx, vy *2+1, My +1, Ry, x, y,
new_val); t[vx] [vy] = T[vx] [vy *2] + T[vx] [vy *2+1];
}
}
void update_x (int vx, int lx, int rx, int x, int y, int new_val) {
if (lx! = rx) {
int mx = (lx + rx) / 2;
if (x <= mx)
update_x (vx *2, Lx, mx, x, y, new_val);
else
update_x (vx *2+1, Mx +1, Rx, x, y, new_val);
}
update_y (vx, lx, rx, 1,0, M-1, X, y, new_val);
}
like "count the number of points lying in a rectangle . "It is understood that in the case of
this task becomes unnecessarily wasteful to build a two-dimensional tree to
elements. Much of this memory will be wasted because each separate point can be reached only in the segments of the
tree segments in the first coordinate, and therefore, the total "useful"
the size of all segments of the trees to the second coordinate is the value .
Then proceed as follows: each node of the tree segments in the first coordinate will be stored
segment tree built only on those second coordinates, which are found in the current interval an
first coordinates. In other words, the construction of the tree segments within some vertex numbered d
boundaries we will consider only those points that fall in this segment ,
and build a segment tree just above them.
Thus we ensure that every segment tree to the second coordinate will take as much
memory as it should. As a result, total memory reduced to .Respond
on inquiry We will continue over Just now when you call the query from a tree on the segments
second coordinate, we'll have to do a binary search on the second coordinate, but it will not worsen the asymptotic behavior.
But the payback would be the impossibility of making an arbitrary modification request: In fact, if a new point,
it will lead to what we would have in any tree segments to the second coordinate add a new element in the
middle that can not be done effectively.
In conclusion, we note that a concise manner described two-dimensional segment tree becomes almost equivalent
above modifications dimensional tree segments (see "Saving all the subarray in each node of the tree segments"). In
particular, it turns out that what is described here
two-dimensional segment tree - it's just a special case of the conservation of the subarray in each
node of the tree where the subarray itself is stored as a tree segments. It follows that if you have to
abandon the two-dimensional tree segments due to inability to perform one or another query, it makes
sense to try to replace the embedded tree segments to any more powerful data structure, for example,
Cartesian tree.
struct vertex {
vertex * l, * r;
int sum;
vertex (int val)
: L(NULL), R(NULL), Sum(val)
{}
vertex * update (vertex * t, int tl, int tr, int pos, int new_val) {
if (tl == tr)
return new vertex (new_val);
int tm = (tl + tr) / 2;
if (pos <= tm)
return new vertex (
update (t-> l, tl, tm, Pos,
new_val), T-> r
);
else
return new vertex ( t-
> l,
update (t-> r, tm+1, Tr, pos, new_val)
);
}
By using this approach can be turned into persistent-data structure virtually any segment tree.
Cartesian tree (treap, deramida)
Cartesian tree - a data structure that combines a binary search tree and binary heap (hence its name, and
the second: treap (tree + heap) and deramida (wood + pyramid).
More strictly, it is a data structure that stores a pair (X, Y) in the form of a binary tree, so that it is a binary search tree
for x and binary pyramid by y. Assuming that all X and Y all
are different, we see that if an element of the tree contains (X0, Y0), then all elements in
left subtree X <X0, all the elements in the right subtree X> X0, and also in the left and right subtree, we
have: Y <Y0.
Deramidy been proposed sitting (Siedel) and Aragon (Aragon) in 1996
Operations
Thus, treap provides the following operations:
● Insert (X, Y) - for O (log N) on the average Performs
the addition of a new element in the tree.
The variant in which the priority value Y is not passed to the function, and is chosen randomly (but bear
in mind that it should not coincide with any other Y in the tree).
● Search (X) - for O (log N) on the average
Searches for an item with the specified key value X. implemented exactly the same as for a
binary search tree.
● Erase (X) - for O (log N) on the average
Searches for an item and removes it from
the tree.
● Build (X1, ..., XN) - for O (N)
Builds a tree from a list of values. This operation can be implemented in linear time (assuming that the values of X1,
..., XN sorted), but here, this implementation will not be discussed.
Here we will use only the simplest implementation - in the form of successive calls Insert, ie for the O (N log N).
● Union (T1, T2) - for O (M log (N / M)) on the average
Combines two trees, on the assumption that all elements are distinct (though this operation can be
implemented with the same asymptotic behavior, if the union need to remove duplicate items).
● Intersect (T1, T2) - for O (M log (N / M)) on the average
Finds the intersection of two trees (ie their common elements). Here is the implementation of this operation will not be considered.
In addition, due to the fact that the Cartesian tree is a binary search tree, and their meanings, to
applicable thereto operations such as finding the K-th largest element and, conversely, the element number determination.
implement the operations need to implement two auxiliary operations: Split and Merge.
Split (T, X) - shared tree T two wood L and R (which is the return value) so manner that L contains all elements
smaller key X, and R contains all the elements are large X. This operation is performed in O (log N). Its
implementation is quite simple - the obvious recursion.
Merge (T1, T2) - combines two subtrees T1 and T2, and returns the new tree. This operation also implemented in O (log N). It
works on the assumption that T1 and T2 have an appropriate order (all
X values in the first than in the second X values). Thus, we need to combine them so as not to
upset the order of priorities for Y. To do this, simply select it as the root of a tree where Y is fundamentally more and
recursively call itself from another tree and the corresponding son selected tree.
Now obvious implementation Insert (X, Y). First down on a tree (as in a normal binary search tree on X), but stay on the first
element, in which the priority value was less than Y. We found the position where we insert our element. Now call Split (X) of
the found element (the element with
with all its subtree), and return it write L and R as the left and right child element to add.
Well understood and implementation Erase (X). Go down the tree (as in a normal binary search tree on
X), looking for the item to be removed. Find the items we simply call it Merge from the left and right sons,
and its return value put in place the item to remove.
Operation Build implement in O (N log N) simply by successive calls Insert.
Finally, the operation Union (T1, T2). Theoretically its asymptotic O (M log (N / M)), but in practice it works very well,
probably with a very small hidden constant. Suppose, without loss of generality, T1-> Y> T2-> Y, ie, T1 is the root of the
root of the result. To get the result, we need to combine the trees T1-> L, T1-> R and T2 in two such tree, so that you can
make sons T1. For this call Split (T2, T1-> X), thereby
we partition the T2 into two halves L and R, which are then recursively combine sons T1: Union (T1-> L, L) and
Union (T1-> R, R), thus we construct the left and right subtrees result.
Implementation
Implement all the operations described above. Introduced here for the convenience of other
designations - priority is indicated prior, values - key.
struct item {
int key, prior;
item * l, * r;
item () {}
item (int key, int prior): key (key), prior (prior), l (NULL), r (NULL) {}
};
typedef item * pitem;
cnt (t-> l) +1. We present new implementations of functions split and merge:
void split (pitem t, pitem & l, pitem & r, int key, int add = 0)
{if (! t)
return void (l = r = 0);
int cur_key = add + cnt (t-> l); / / Calculate the
implicit key if (key <= cur_key)
split (t-> l, l, t-> l, key, add), r = t;
else
split (t-> r, t-> r, r, key, add + 1 + cnt (t-> l)), l =
t; upd_cnt (t);
}
We now turn to the implementation of various miscellaneous operations on implicit Cartesian trees:
● Inset element.
Suppose we need to insert an element in the position pos. We divide the Cartesian tree into two halves: the proper array [0
.. pos-1] and array [pos .. sz]; it is enough to cause a split (t, t1, t2, pos). Then we can combine the tree t1 with a new vertex;
it is enough to cause a merge (t1, t1, new_item) (easy to see that all the preconditions to merge met). Finally, combine the
two trees t1 and t2 back into a tree t -
calling merge (t, t1, t2).
● Removal element.
It is even easier: just find item to be removed, and then perform the merge for his sons l and r, and put
in place the result of combining the top of t. In fact, the removal of the implicit Cartesian tree is different
from the usual Cartesian removal of the tree.
● Sum / minimum etc. on the interval.
First, for each vertex will create an additional field f in the structure item, which will be stored in the objective function value for
the subtree of this vertex. Such a field is easy to maintain, this should be analogous support sizes cnt (create a function that
computes the value of the field, using his values for the sons and insert calls to this function at the end of all the functions that
change the tree). Secondly, we need to learn to respond to a request for an arbitrary interval [A; B]. Learned to distinguish from
a tree
part corresponding to the segment [A; B]. It is easy to understand that it is sufficient to cause at first split (t, t1, t2, A),
and then split (t2, t2, t3, B-A +1). As a result of the tree and t2 will consist of all the elements in the interval
[A; B], and only them. Consequently, the answer to the query will be in the top of the field f t2. After
answering the query tree should restore calls merge (t, t1, t2) and merge (t, t, t3).
● Addition / Paint on the interval.
Here we proceed as in the previous paragraph, but instead of f will store field add, and which will contain the added value
(or values, which paint the entire subtree of the vertex).
Before performing any operation should add this value to "push" - ie amended accordingly tl-> add and t-> r-> add, and have
a value add remove. Thus, we will ensure that no changes
tree information will not be lost.
● Revolution on the interval.
This item is almost identical to the previous - you need to enter a field bool rev, which put in a true, when
want to make a revolution in the subtree of current node. "Push" field rev is that we exchange places sons
current node, and set the flag for them.
Implementation. An example, the full realization of the implicit Cartesian tree with the coup on segment.
Here each vertex is stored as a field value - the actual value of the element present in the array at the current
position. Also shows the implementation of the function output (), which displays an array corresponding to
the current state of implicit Cartesian tree.
void split (pitem t, pitem & l, pitem & r, int key, int add = 0)
{if (! t)
return void (l = r = 0);
push (t);
int cur_key = add + cnt (t->
l); if (key <= cur_key)
split (t-> l, l, t-> l, key, add), r = t;
else
split (t-> r, t-> r, r, key, add + 1 + cnt (t-> l)), l =
t; upd_cnt (t);
}
It is clear that while finding a minimum throughout the stack will be just in taking values stack.top (). Second.
It is also obvious that when you add a new item to the stack second value will be equal to min (stack.top (). Second,
new_element). Removing an item from the stack is no different from the usual stack of removal as a removable element
could not affect the second value for the remaining elements.
Implementation:
● Adding an element:
● Removing elements:
● Finding a minimum:
● Finding a minimum:
● Adding an element:
● Removing elements:
It is clear that the average execution time of all these operations is O (1).
Implementation:
● Finding a minimum:
● Adding an element:
● Removing elements:
if (s2.empty ())
while (! s1.empty ()) {
int element = s1.top ().
first; s1.pop ();
int minima = s2.empty ()? element: min (element, s2.top
(). Second);
s2.push (make_pair (element, minima));
}
result = s2.top ().
first; s2.pop ();
The problem of finding a minimum in all
subsegments fixed length of this array
Suppose we are given an array A of length N, and is given by M ≤ N. find the minimum
required in each subsegments of length M of the given array, ie find:
Heap is a binary tree, for each vertex of which is true, that the value in the top less than or equal value in all its
descendants (it's a lot for a minimum; course, you can define a bunch of symmetrically to the maximum). Thus, the root
is always a minimum heap.
Standard set of operations defined for the piles as follows:
● Adding an item
● Finding the minimum
● Removing the minimum (remove it from the tree and return its value)
● Merge two heaps (back pile containing elements of both heaps, duplicates are not removed)
● Deleting an arbitrary element (at a known position in the tree)
Randomized bunch lets you perform all of these operations within the expected time at very
simple implementation.
Data structure
Directly describe the data structure that describes a binary heap:
struct tree {
T value;
tree * l, * r;
};
In the top of the tree is stored value a type for which the operator is defined
comparison ( ). Additionally, pointers to the stored left and right Sons (which are equal to 0,
if the corresponding son is missing).
Performing
It is easy to understand that all operations on a bunch reduced to a single operation: merger one of the two piles. Indeed,
adding an element to a heap equivalent merge this heap with a heap consisting of a single element to be added. Finding
the minimum does not require any action - is just the minimum root of the heap. Removing the minimum equivalent to the
result of a merger pile replaced the left and right subtrees of the root. Finally, the removal of an arbitrary element similar to
the removal of the minimum:
the entire subtree rooted at this vertex is replaced by the result of the merger of two sons-subtrees this vertex.
So we actually need to implement the merge operation only two piles, all other operations are
reduced to trivial this operation.
Given two heaps and want to return their union. It is clear that the root of each of these piles minima are blocked so
radically resultant heap will be a minimum of these two values. So, we compare the root of what is heaps less important it is
placed in the root of the result, and now
we must unite sons selected vertex with the remaining bunch. If we choose some feature of one of the two
sons, then we will have to simply combine the subtree root with this son of a heap. Thus, we again come to
a merge operation. Sooner or later, this process will stop (it will take, of course, no more than the sum of the
heights of piles).
Thus, to achieve an average logarithmic asymptotics, we need to specify the method of selecting one of the
two sons, so that the average length of the path traveled by the order would be obtained from the logarithm
of the number of elements in the heap. It is not difficult to guess that make this choice, we will
accidentallyThus, the implementation of the merge operation is obtained as follows:
It first checks if at least one of the piles is empty, then no action is not necessary to produce fusion. Otherwise, we do to heap
was a bunch with a smaller value in the root (which exchange the and If necessary). Finally, we believe that the second
bunch will merge with the left child of the root heap So we randomly exchange the left and right children, and then execute
the merger left child and a second heap.
Asymptotics
Introduce the random variable denoting length of random paths from root to leaf (length in number
of edges). It is understood that the algorithm performed for operations. Therefore, to study
the asymptotic behavior of the algorithm is necessary to investigate the random variable .
Expectation value
It is argued that the expectation is bounded above by the logarithm of the number of vertices in this pile:
- Respectively the left and right subtrees of the root of the heap,
This can be proved easily by induction. Let and
an
d - The number of vertices in them (of course, ).
Then we have:
QED.
We denote the set of paths from the root to the leaf pile, the length of which exceeds .
Note that for any path length likelihood as random path will be selected because it
equal . Then we obtain:
QED.
Apps
In addition to direct application in a variety of tasks are the following: ● Task LCA
Decision
RMQ problem is solved by data structures.
Described on the website of the data structures you can choose:
● Sqrt-decomposition - responds to a request for O (sqrt (N)), preprocessing for O (N). The
advantage is that it is very simple data structure. Disadvantage - asymptotics.
● Segment tree - responds to a request for O (log N), preprocessing for O (N).
Advantage - good asymptotic behavior. Disadvantage - a lot of code compared to other data structures.
● Fenwick tree - responds to a request for O (log N), preprocessing for O (N log N)
Advantage - written very quickly and works very fast too. But a significant drawback - Fenwick tree can only
respond to requests from the L = 1, which is applicable to many applications.
Note. "Preprocessing" - a pre-processing of the array A, it actually build a data structure for this
array.
Now suppose that the array A may vary during operation (ie, will also do requests for changes in values in an interval [L; R]).
Then, the resulting problem can be solved by
Sqrt-decomposition and Tree segments.
Finding of the longest increasing
subsequence
Condition of the problem following. An array of Numbers:. Locate in this sequence is strictly
increasing subsequence of greatest length.
This article discusses the various algorithms to solve this problem, as well as some of the tasks
that can be reduced to this problem.
By combining these two one embodiment obtain a final algorithm for calculating :
Implementation
We present the implementation of the algorithm described above, which finds and displays
the length of the longest increasing subsequence:
Recovery Answer
While we only have learned to look for the long answer, but very of the longest subsequence, we can not deduce, since
it does not maintain any additional information about where the maximum occurs.
To be able to restore the response, in addition to the
dynamics of also need to keep an auxiliary
array - That, in any location peaked for each value . In other words, the index
will denote the same index for which to obtain the greatest value . (This array
dynamic programming is often referred to as "ancestors array".)
Then, in order to deduce the answer, you just have to go on the element with the maximum
value by his ancestors as
long as we do not derive all subsequence, ie until we reach the element with a value of .
minus one that a little bit easier when you restore an answer.
int d[MAXN], P[MAXN]; / / constant MAXN equal to the largest possible value of
n
absolute coincidence logic program in the counting process and the dynamics of the recovery process.
Solution for : Dynamic Programming
with binary search
To get a quick solution to the problem, we construct another option for dynamic programming,
int d[MAXN];
d[0] =-INF;
for (int i =1; i <= n; +
+ I) d[i] = INF;
for (int i =0; i <n; i + +)
for (int j =1; j <= n; j + +)
if (d[j-1] <A[i] && A[i] <D[j])
d[j] = A[i];
Now note that this dynamic is one a very important property: for
all . Another property - that every element updates the maximum single cell .
Thus, it means that the next process we can for Making a binary search
the first number, which is strictly greater
the array. In fact, we are just looking in the array than And
try to update this element is similar to the above realization.
Implementation for
Using standard in the language C + + binary search algorithm (which returns the position of the first
element is strictly greater part), we obtain a simple implementation:
int d[MAXN];
d[0] =-INF;
for (int i =1; i <= n; +
+ I) d[i] = INF;
for (int i =0; i <n; i + +) {
int j = int (upper_bound (d.begin(), D.end(), A[i]) - D.begin());
if (d[j-1] <A[i] && A[i] <D[j])
d[j] = A[i];
}
Recovery Answer
For such dynamics also can restore the answer, which again, in addition to speakers also need to store an array of
"ancestors" - is the element with which the index ends optimal subsequence of length . In addition, for each element
of the array will have to keep it "ancestor" - ie the index of the element that must precede in the optimal
subsequence.
By keeping these two arrays during the dynamics calculations, at the end it will be easy
to restore the desired subsequence.
(It is interesting to note that with regard to the dynamics of this response can be restored only way through arrays ancestors -
and without them recover after calculating the response dynamics will be impossible. This is one of the rare cases where the
dynamics of an alternative method of recovery is not applicable - no arrays ancestors).
Consequently, if we through denote such arrayIn which we write the value of the dynamics of the numbers:
it turns out that all that we need to be able to - to look for it maximum prefix array
: .
The task of finding the maximum on a prefix of the array (given the fact that the array can be changed) solved many
Related tasks
We present here a few problems closely related to the task of finding of
the longest increasing subsequence.
Apparently, the way to solve through the dynamics for to this problem can not be applied.
The basic idea - the idea to use quicksort. Actually, the algorithm is simple, difficult to prove that it runs in
average O (N), in contrast to quicksort.
if (r <= l +1)
{
// current portion consists of 1 or 2 elements -
// can easily find the answer
if (r == l +1 && a [r] <a
[l]) swap (a [l], a
[r]);
return a [k];
}
// perform division
// barrier is a [l +1], ie, median of a [l], a [l +1],
a [r]
unsigned
i = l
+1, j =
r;
const T
cur = a [l +1];
for (; ;)
{
while (a [+ + i]
<cur); while (a [- j]>
cur); if (i> j)
break;
swap (a [i], a
[j]);
}
// barrier insert a
[l +1] = a [j];
a [j] = cur;
It should be noted that in the standard C + + library, this algorithm has already been implemented - it is called nth_element.
Finding of the longest increasing
subsequence
Condition of the problem following. An array of Numbers:. Locate in this sequence is strictly
increasing subsequence of greatest length.
This article discusses the various algorithms to solve this problem, as well as some of the tasks
that can be reduced to this problem.
By combining these two one embodiment obtain a final algorithm for calculating :
Implementation
We present the implementation of the algorithm described above, which finds and displays
the length of the longest increasing subsequence:
Recovery Answer
While we only have learned to look for the long answer, but very of the longest subsequence, we can not deduce, since
it does not maintain any additional information about where the maximum occurs.
To be able to restore the response, in addition to the
dynamics of also need to keep an auxiliary
array - That, in any location peaked for each value . In other words, the index
will denote the same index for which to obtain the greatest value . (This array
dynamic programming is often referred to as "ancestors array".)
Then, in order to deduce the answer, you just have to go on the element with the maximum
value by his ancestors as
long as we do not derive all subsequence, ie until we reach the element with a value of .
minus one that a little bit easier when you restore an answer.
int d[MAXN], P[MAXN]; / / constant MAXN equal to the largest possible value of
n
absolute coincidence logic program in the counting process and the dynamics of the recovery process.
Solution for : Dynamic Programming
with binary search
To get a quick solution to the problem, we construct another option for dynamic programming,
int d[MAXN];
d[0] =-INF;
for (int i =1; i <= n; +
+ I) d[i] = INF;
for (int i =0; i <n; i + +)
for (int j =1; j <= n; j + +)
if (d[j-1] <A[i] && A[i] <D[j])
d[j] = A[i];
Now note that this dynamic is one a very important property: for
all . Another property - that every element updates the maximum single cell .
Thus, it means that the next process we can for Making a binary search
the first number, which is strictly greater
the array. In fact, we are just looking in the array than And
try to update this element is similar to the above realization.
Implementation for
Using standard in the language C + + binary search algorithm (which returns the position of the first
element is strictly greater part), we obtain a simple implementation:
int d[MAXN];
d[0] =-INF;
for (int i =1; i <= n; +
+ I) d[i] = INF;
for (int i =0; i <n; i + +) {
int j = int (upper_bound (d.begin(), D.end(), A[i]) - D.begin());
if (d[j-1] <A[i] && A[i] <D[j])
d[j] = A[i];
}
Recovery Answer
For such dynamics also can restore the answer, which again, in addition to speakers also need to store an array of
"ancestors" - is the element with which the index ends optimal subsequence of length . In addition, for each element
of the array will have to keep it "ancestor" - ie the index of the element that must precede in the optimal
subsequence.
By keeping these two arrays during the dynamics calculations, at the end it will be easy
to restore the desired subsequence.
(It is interesting to note that with regard to the dynamics of this response can be restored only way through arrays ancestors -
and without them recover after calculating the response dynamics will be impossible. This is one of the rare cases where the
dynamics of an alternative method of recovery is not applicable - no arrays ancestors).
Consequently, if we through denote such arrayIn which we write the value of the dynamics of the numbers:
it turns out that all that we need to be able to - to look for it maximum prefix array
: .
The task of finding the maximum on a prefix of the array (given the fact that the array can be changed) solved many
Related tasks
We present here a few problems closely related to the task of finding of
the longest increasing subsequence.
Apparently, the way to solve through the dynamics for to this problem can not be applied.
Task "Parquet"
There is a rectangular area the size NxM. Need to find a number of ways to pave this area figures 1x2 (empty cells
should not remain, figures should not overlap).
Construct a dynamics: D [I] [Mask], where I = 1 .. N, Mask = 0 .. 2 ^ M-1. I denotes the number of lines in the current field, and Mask -
profile the last line in the current field. If the j-th bit in the Mask is equal to zero, then the spot profile extends
at a "normal level", and if 1 - here "seizure" depth 1. answer, obviously, is D [N] [0].
Build this momentum going, just going through all the I = 1 .. N, all masks Mask = 0 .. 2 ^ M-1, and for each
mask will skip forward, ie add to it a new shape in all possible ways.
Implementation:
int n, m;
vector <vector <long long>> d;
int main ()
{
cin >> n >> m;
}
Finding the greatest zero submatrix
Given a matrix size . It is required to find a submatrix consisting of all zeros and all of these - having the
largest area (sub-matrix - a rectangular area of the matrix).
Trivial algorithm - fingering desired submatrix - even with the implementation of good will
work . Below we describe an algorithm that works for Ie in linear relative
size of the matrix time.
Algorithm
To resolve ambiguities immediately observe that equals the number of rows of the matrix Respectively, - Is
number of columns. Elements of the matrix will be numbered in -Indexing, ie in the designation Indexes
and
run ranges , .
/ / D calculated for the i-th row, we can use these values here
}
Value each is equal to a value lying in the moment the top of stack.
Clearly, as additions to the stack on each line occurs exactly pieces, then deletes and also could
not be more so in the sum of the asymptotic behavior is linear.
Dynamics to find indices considered to be similar, but it is necessary to view the columns
right to left.
Also note that this algorithm consume memory (not counting the input data - matrix ).
Implementation
This implementation of the above algorithm reads the dimensions of the matrix, then the matrix
itself (as a sequence of numbers separated by spaces or line breaks), and then outputs the
answer - the size of the largest zero submatrix.
Easy to improve this implementation so that it also outputs a zero submatrix itself: it is necessary at
each change remember also row and column of the submatrix (they will be respectively ,
, , ).
int n, m;
cin >> N >> m;
vector <vector <int>> A (n, vector
<int>(m));for (int i =0; i <n; + + I)
for (int j =0; j <m; +
+ J) cin >>
A[i] [j];
int ans = 0;
vector <int> D (m, -1), D1 (m), D2
(m); stack <int> St;
for (int i =0; i <n; + + I) {
for (int j =0; j <m; + +
J) if (a[i] [j]
== 1)
d[j] = I;
while (! St.empty())
st.pop(); for (int j =0; j
<m; + + J) {
while (! St.empty() && D[st.top()] <= D[j]) st.pop();
d1[j] = St.empty() ? -1 : St.top();
st.push (j);
}
while (! St.empty())
st.pop(); for (int j = m-1;
j> =0; - J) {
while (! St.empty() && D[st.top()] <= D[j]) st.pop();
d2[j] = St.empty() ? m: st.top();
st.push (j);
}
for (int j =0; j <m; + + J)
ans = max (ans, (i - d[j]) * (d2[j] - D1[j] - 1));
}
cout << Ans;
Gauss solution of linear equations
Given a system linear algebraic equations (SLAE) with unknown. Required to solve this system:
to determine how many solutions it has (none, one, or infinitely many), and if it has at least one
solution, then find any of them.
Formally problem is formulated as follows: solve the system:
where - Matrix Composed of the coefficients , and - The height of the column vectors .
It should be noted that the linear algebraic equation may not be over the real numbers, and over a
field modulo any number of , Ie:
- Gauss algorithm works for such systems too (but this case will be discussed below in a separate section.)
Gauss
Strictly speaking, the method described below correctly called by the "Gauss-Jordan" (Gauss-Jordan
elimination), because it is a variation of the Gauss method described by Jordan surveyor William in 1887 (it is worth noting that William
Jordan is not the author of any of Jordan's theorem on curves or Jordan algebra
- All three different scientists namesake; moreover, appear to be more correct spelling is "Jordan", but writing "Jordan" is rooted in
the Russian literature). It is also interesting to note
that simultaneously by Jordan (according to some sources even before him) came up with this algorithm Klasen (B.-I. Clasen).
line is divided into And then subtracted from all other rows with such coefficients,
to nullify the second column of the matrix .
And so on, until we process all rows or all columns of the matrix . If , Then by the construction of
the algorithm is obvious that the matrix turn the unit that we required.
Note that the row permutation is realized much easier on a computer than a permutation of the columns: for the exchange
of some places two columns must remember that these two variables have exchanged places, and then, in the reduction
of the response to correctly recover what the answer to which the variable refers . When changing strings, no such
additional actions are necessary.
Fortunately for the correctness of the method alone is sufficient exchange lines (so-called "partial pivoting", unlike "full pivoting",
and when the exchange lines and columns). But what exactly is the line should be selected for the exchange? And is it true that
the search for the reference element should be done only when the current element zero?
General answer to this question does not exist. There are a variety of heuristics, but the most effective of them (the
ratio of simplicity and efficiency) is that heuristic: As a reference element to be
taking the highest modulo element, said supporting element search and it is necessary to exchange
alwaysAnd not only when necessary (i.e. not only when ).
In other words, before performing Phase II of the Gauss-Jordan elimination with partial pivoting
heuristics to be found in -Th column among the elements with indices from to maximum modulus,
and swap line with this element with -Th row.
First, this heuristic will solve SLAE, even if in the course of decision would happen so that the element
. Second, which is very important, this heuristic improves numerical stability Gauss-
Jordan.
Without this heuristic, even if the system is such that each second phase - Gauss-
Jordan will work, but eventually accumulating error may be so great that even for matrices of size about
error will exceed the answer itself.
Degenerate cases
So, if you stay on the Gauss-Jordan algorithm with partial pivoting, is approved if and nevrozhdena system (ie has
a nonzero determinant, which means that it has a unique solution), then the above algorithm will work fully and come to the
identity matrix (Proof of this, ie, that a non-zero reference element will always be, is not presented here).
We now consider general case - When and are not necessarily equal. Assume that the support element on the
Th step has not been found. This means that Th column of all rows from the current contain zeros. It is alleged that in this
case the Th variable can not be determined, and a independent variable (Can take an arbitrary value). To the Gauss-
Jordan continued his work for all subsequent variables in such a situation should just skip the current -Th column, without
increasing the current line number (we can say that we remove virtually Th column of the matrix).
So, some of the variables in the process of the algorithm can be delivered independent. It is
understood that when the amount of more than the number of variables equations, then
at least independent variables show up.
In general, if it finds at least one independent variable, it can take an arbitrary value, while the other (dependent)
variables are expressed through it. This means that when we are working in the field of real numbers, the system is
potentially infinitely many solutions (If we consider the linear algebraic equation modulo the number of solutions is
equal to this module raising the number of independent variables). However, we must be careful: it is necessary to
remember that even if the independent variables were found, nevertheless SLAE may have no solutions at all.
This happens when the remaining untreated equations (those to which the Gauss-Jordan is not reached, ie this equation, in which there
were only independent variables) have at least one non-zero free term.
However, it is easier to check the explicit substitution of the solution found: all independent variables
set to zero, the dependent variables to assign values found, and substitute this solution in current
Slough.
Implementati
on
We present here the implementation of Gauss-Jordan heuristics with partial pivoting (choice of the
support member as maximum of the column).
The input function system passed the matrix itself . The last column of the matrix - It is in our
the old notation column free coefficients (as done for the convenience of programming - as in the algorithm, all
operations with free coefficients repeat the operations with the matrix ).
The function returns the number of solutions of the system ( , or ) (Infinity is indicated in the
special code constant , Which can be any big difference). If at least one solution exists, then
it returns to the vector .
The functions supported by two pointers - the current column and current row .
Starts as a vector in which each variable is recorded in what line it should happen (in other words, for each column
written line number in which this column is different from zero). This vector is needed because some variables could not
"determine" in the solution (ie, it
independent variables, which can be given an arbitrary value - for example, here is the implementation
of zeros).
The implementation uses the technique of partial pivoting, producing a search string with a maximum modulo element
and then rearranging the row position (Although obvious permutation of the rows can be replaced by the exchange of two indices
from an array, in practice it does not give the real payoff, because spent on exchanges operations).
In order to ease the implementation of the current line is not divisible by the support member - with the result
that at the end of the algorithm becomes the unit matrix and the diagonal (however, apparently dividing line
allows the drive element to reduce some errors occur).
After finding a solution to it is inserted back into the matrix - to check whether the system has at least one solution or not. If the
Asymptotics
We estimate the asymptotic behavior of this algorithm. The algorithm consists of phases, each of which takes
place:
● search and a rearrangement of the support member - during using heuristics "partial
pivoting "(maximum search in the column)
● if the reference element in the current column was found - that the addition of this
equation to all other equations - during
Obviously, the first paragraph has asymptotics less than a second. Note also that the second paragraph is
performed no more than times - as much as can be dependent variables in Slough.
operations.
Additions
Of course, now it becomes unnecessary to use any artful technique of choice supporting element
- is sufficient to find any nonzero element in the current column.
If the module is a simple, no complications do not arise - occurring in the course of Gauss division
does not create any problems.
Especially remarkable module equal to twoFor him, all operations can be performed with the matrix is very efficient.
For example, a single row from substraction other modulo two - this is actually their
symmetric difference ("xor"). Thus, the entire algorithm can be greatly accelerated by squeezing the entire matrix in
bit masks and their terms only. We present here a new implementation of the main part of the
Gauss-Jordan using standard container C + + "bitset":
int gauss (vector <bitset <N>> a, int n, int m, bitset <N> & ans) {
vector <int> Where (m, -1);
for (int col =0, Row =0; col <m && row <n; + +
Col) { for (int i = row; i <n; + + I)
if (a[i] [col]) {
swap (a[i],
A[row]);break;
}
if (! a[row]
[col]) continue;
where[col] = Row;
for (int i =0; i <n; + + I)
if (i! = row && a[i]
[col]) a[i] ^ =
A[row];
+ + Row;
}
As you can see, the implementation has become even a little shorter, though it is much faster than the old implementation
- Namely, the faster times due to the bit compression. It should also be noted that the solution of
systems modulo two in practice is very fast, since cases when from one row should take another, occur
infrequently (for sparse matrices, this algorithm can work during most of the order of the size of a square
than a cube).
If the module arbitrary (Not necessarily simple), then everything becomes more complicated. It is clear that using
Chinese Remainder Theorem, We reduce the problem with an arbitrary module to the modules only
type "prime power". [Text was further obscured because is anecdotal - perhaps the wrong
way to solutions]
Finally, consider the question number of solutions of linear algebraic equation modulo. The
answer is quite simple: the number of solutions is Wherein - Module - The number of
independent variables.
A little bit about the different methods of selecting the reference element
As mentioned above, the simple answer to this question is no.
Heuristics "partial pivoting", which was to find the maximum element in the current column, actually works quite well. It also turns
out that it gives almost the same result as the "full pivoting" -
when the support member is sought among the elements of the entire submatrix - starting with the current row and the current column.
But it is interesting to note that both of these heuristics with the search for the maximum element, in fact, are very dependent
on how the original equations were scaled. For example, if one of the equations of the system multiplied by a million, then this
equation is almost certain to be selected as a presenter at the first step. This seems rather strange, so logical transition to a
little more complicated heuristics - so
called "Implicit pivoting".
Heuristics implicit pivoting is that the elements of the various lines are compared as if both lines were
normalized so that the maximum modulus element therein would be unity. To implement this technique, it
is necessary to maintain a current maximum in each row (each line or support so that it was at a maximum
is equal to unity modulus, but this can lead to an increase in the accumulated error).
This method allows several to extend the set of problems solved by Gauss-Jordan algorithm
with an acceptable margin of error.
Literature
● William H. Press, Saul A. Teukolsky, William T. Vetterling, Brian P. Flannery. Numerical
Recipes: The Art of Scientific Computing [2007]
● Anthony Ralston, Philip Rabinowitz. A first course in numerical analysis [2001]
Finding the rank of a matrix
Rank of the matrix - this is the largest number of linearly independent rows / columns of the matrix.
Rank is not defined only for square matrices; let the matrix is rectangular and has a size NxM.
Also rank matrix can be defined as the maximal order minors of the matrix are different from zero.
Note that if the matrix is square and its determinant is nonzero, then the rank is equal to N (= M), or it will
be less. In the general case, the rank of the matrix is not greater than min (N, M).
Algorithm
Search rank can be modified using Gauss method. Will perform exactly the same
operations in the solution of the system or finding its determinant, but if at any step in the i-th column of this row to the unselected no
nonzero, then we skip this step, and the rank is decremented (initially set equal rank max ( N, M)). Otherwise, if we have found on the
i-th step of the string with a nonzero element in the i-
th column, then mark the line as selected, and perform common operations subtraction of this line from the rest.
Implementation
Algorithm
Use the ideas Gauss method for solving systems of linear equations.
Will perform the same steps as when solving a system of linear equations, excluding only the division of the current line to
a [i] [i] (more precisely, the division itself can be carried out, but implying that the number shall be made for the sign of the
determinant). Then all operations that we will produce a matrix will not change the value of the determinant of the matrix,
except, perhaps, a sign (we only exchange the places of two lines
that changes to the opposite sign, or we add one line to the other, that does not change the value of the determinant).
But the matrix to which we arrive after Gauss, is diagonal and its determinant is the product of the elements on the diagonal. Sign,
as already mentioned, will be determined by the number of exchange lines (if odd, the sign of the determinant should be changed
to the opposite).
Thus, we can use Gauss to calculate the determinant of a matrix in O (N3).
It remains only to note that if at some point we do not find in the current column nonzero, then the
algorithm should stop and return 0.
Implementation
double det = 1;
for (int i = 0; i <n; + +
i) {int k = i;
for (int j = i +1; j <n; + + j)
if (abs (a [j] [i])> abs (a [k]
[i])) k = j;
if (abs (a [k] [i]) <EPS)
{det = 0;
break;
}
swap (a [i], a
[k]); if (i! = k)
det =-det;
det * = a [i] [i];
for (int j = i +1; j <n; +
+ j) a [i] [j] / =
a [i] [i];
for (int j = 0; j <n; + + j)
if (j! = i && abs (a [j] [i])>
EPS) for (int k = i +1; k
<n; + + k)
a [j] [k] - = a [i] [k] * a [j] [i];
}
Actually algorithm finds Kraut decomposition of the matrix A as A = L UWhere L - the lower and U -
upper triangular matrix. Without loss of generality, we can assume that all the diagonal elements of L
are equal to 1. But knowing these matrices, it is easy to calculate the determinant of A: it is the
product of all the elements on the main diagonal of the matrix U.
There is a theorem, according to which every invertible matrix has LU-decomposition, and uniquely, if
and only if all its principal minors are nonzero. It will be recalled that we consider only such
expansions in which the diagonal L consists only of units; otherwise the same, generally speaking, the
decomposition is not unique.
Implementation
Code Java (using fractional arithmetic long):
try {
int sign = 1;
if (j! = imax)
{
BigDecimal t = scaling
[imax]; scaling [imax] =
scaling [j]; scaling [j] = t;
sign =-sign;
}
if (j! = n-1)
for (int i = j +1; i <n; i + +)
a [i] [j] = a [i] [j].
divide
(A [j] [j], 100
BigDecimal.ROUND_HALF_EVEN);
return result.divide
(BigDecimal.valueOf (1), 0,
BigDecimal. ROUND_HALF_EVEN). ToBigInteger ();
}
catch (Exception e)
{
return BigInteger.ZERO;
}
}
Integration by Simpson's formula
Required to calculate the value of the definite integral:
The solution described here, was published in one of theses Thomas Simpson (Thomas Simpson)
in 1743
Simpson's formula
Let - A natural number. Divide the interval of integration on Equal parts:
Now calculate the integral separately on each of the segments And then add all the values.
. Replace the
So, let us consider the next segment function on
It parabola passing through the three points . This parabola always exists and is unique.
It can be found analytically, then left only to integrate an expression for it, and we finally obtain
Adding these values in all segments, we obtain the final Simpson formula:
Error
The error is given by Eq Simpson, do not exceed the quantities:
Implementation
Here - Some user-defined function.
double a, b; / / input
const int N = 1000*1000; / / number of steps (Already multiplied
2) double s = 0;
double h = (b - a) / N;
for (int i =0; i <= N; + +
I) {
double x = a + h * i;
s + = f(x) * ((i ==0 | | I == N) ? 1 : ((i &1)==0) ? 2 : 4);
}
s * = h / 3;
Newton's method (tangent) to find the roots
This iterative method invented Isaac Newton (Isaak Newton) about 1664, however, this method is sometimes referred to
by the Newton-Raphson (Raphson), as Raphson invented the same algorithm
a few years later Newton, but his article was published much earlier. The task
Required to solve this equation precisely find one of its roots (assuming the root exists). It is assumed that
Algorithm
Input parameter of the algorithm, except for the function is also the initial approximation - Some
, From which the algorithm starts to go.
Suppose that we have calculated Calculate follows. Draw a tangent to the graph of the function in
point And find the point of intersection of this tangent with the horizontal axis. is set equal to
the found point, and repeat the whole process from the beginning.
It is easy to obtain the following formula:
Intuitively, if the function is quite "good" (smooth) and is sufficiently close to the root, then will be
even closer to the required root.
The rate of convergence is quadraticThat, relatively speaking, means that the number of
accurate digits in the approximate value doubles with each iteration.
The first exemplary embodiment of the problem - when given a fractional number And the need
to calculate its root with some accuracy :
double n;
cin >> N;
const double EPS = 1E-15;
double x = 1;
for (,;) {
double nx = (x + n / x) / 2; if
(abs (x - nx) <EPS) break; x =
nx;
}
printf ("% .15 Lf", X);
Another common problem - when you want to calculate the integral root (part find the greatest such that). Here
we have a little change the stop condition of the algorithm,
since it may happen that starts "jumping" near response. Therefore, we add the condition that if the value decreased in
the previous step and the current step is trying to increase, the algorithm must be stopped.
int n;
cin >> N;
int x = 1;
bool decreased = false;
for (,;) {
int nx = (x + n / x) >> 1;
if (x == nx | | nx> x && decreased) break;
decreased = nx <x;
x = nx;
}
cout << X;
Finally, we have a third option - in the case of long arithmetic. Since the number of can be quite large, it makes sense to pay
attention to the initial approximation. Obviously, the closer to the root of it, the faster the results. Quite simple and efficient to take
as
The number of initial approximation, where - The number of bits including . Here is the code in the language
Java, demonstrates this option:
BigInteger n; / / input
BigInteger a = BigInteger.ONE.shiftLeft (n.bitLength() / 2);
boolean p_dec = false;
for (,;) {
BigInteger b = n.divide(a). Add(a).
ShiftRight(1);
if (a.compareTo(b)== 0 | | A.compareTo(b)< 0 && P_dec)break;
p_dec = a.compareTo(b) > 0;
a = b;
}
For example, this embodiment is performed for the code
number in milliseconds, and if you remove the improved
selection of the initial approximation (just start with ) Will be executed approximately milliseconds.
Ternary search
Algorithm
Take any two points and in this segment: . Calculate the value of
the function and . Next we get three options:
● If you find that , The required maximum can not be located on the left side, ie in parts
. This is easily seen if the left point function is smaller than the right, then either of these two points are
in the "lift" function, or only the left point is there. In any case, it means that
maximum further makes sense to look only in the interval .
● If, conversely, , The situation is similar to the previous up to the symmetry. Now
desired maximum may not be in the right portion, i.e. in parts , So go to the segment .
● If the Then either of these two points are at the maximum, or a point located in the left
increasing field, and right - in descending order (essentially used here is that the increase /
strict decrease). Thus, in a further search must be performed in the interval , But (
To simplify code), this case can be attributed to any of the previous two.
Thus, according to the comparison result of the function at two internal points instead of the current segment we
find a new search segment. Now repeat all the actions for this new segment, again
obtain a new, strictly smaller segment, etc.
Sooner or later the length of the segment will be a little smaller than a predetermined constant-precision, and the process
can be stopped. This method is numerical, so after stopping the algorithm can assume approximately that at all points
- Choose points so that the segment shared them into 3 equal parts:
However, a different choice when and closer together, convergence rate will increase slightly.
Here - in fact, absolute error Answer (not counting errors due to inaccurate calculation functions).
Instead of the criterion "while (r - l> EPS)" and you can choose a stopping criterion:
On the one hand, it is necessary to choose the constant To provide the desired precision (typically
quite a few hundred to achieve maximum accuracy.) But, on the other hand, by
iteration ceases to depend on the absolute values and Ie we are actually using
Set the desired relative error.
Binomial coefficients
Binomial coefficient is the amount of ways to choose a set of Objects of various items, excluding the order of these
elements (ie the number of unordered collections).
It is believed that this formula, like a triangle, to effectively find coefficients opened Blaise Pascal (Blaise Pascal), who lived
in the 17th century. Nevertheless, she was known to the Chinese mathematician Yang Hui (Yang Hui), who lived in the 13th
century. Perhaps it was opened by the Persian scholar Omar Khayyam (Omar Khayyam). Moreover,
Indian mathematician Pingala (Pingala), still lived in the 3rd century. BC, got similar results. Newton is a merit that he
Calculation
An analytic formula to compute:
This formula can be deduced from the problem of the disordered sample (number of ways to randomly select items from elements.)
First, calculate the number of ordered samples. Select the first element is
Ways second - Third - And so forth. As a result, the number of samples we get ranked
formula. To disordered samples easily go if we notice that each disordered sample corresponds exactly
ranked (since
is the number of possible permutations elements.) As a result, by dividing on , We obtain
Properties
Binomial coefficients have many different properties, present the most simple of them:
● Rule of symmetry:
● Adding-imposition:
● Summation over :
● Summation over :
● Weighted summation:
An improved
It can be noted that the above implementation of the numerator and the denominator is the same number of factors
(), each of which is less than unity. Therefore, we can replace our product at a fraction of fractions, each of which is
real-valued. However, you will notice that after the multiplication of the current response to each another fraction will
still receive an integer (this, for example, follows from the properties of the "make-issuance"). Thus, we obtain the
following realization:
Here we present a fractional number carefully to the whole, given that due to accumulated errors it may be slightly less
than the true value (eg, instead of three).
Pascal's Triangle
Using the same recurrence relation can build a table of binomial coefficients (in fact, Pascal's triangle), and take from it the
result. The advantage of this method is that the intermediate results never exceed the answer and for the calculation of each
new element of the table need only one addition. The disadvantage is the slow operation for large N and K, if in fact the table is
not necessary, and need a single value (because you will need to calculate the build
If the entire table of values is not necessary, it is easy to see enough of it to keep only two lines (the current
- -Th row and the previous - Th.)
Calculation of O (1)
Finally, in some situations is beneficial predposchitat advance the values of all the factorials, in order to subsequently assume
any necessary binomial coefficient, producing only two divisions. This may be advantageous when using Long arithmeticWhen
memory does not allow the entire predposchitat
Pascal's triangle, or when it is required to make payments for some simple module (if it is not simple, then there are difficulties in
dividing the numerator by the denominator and can be overcome if the factoring module and store all the numbers in the form of
vectors of the degrees of these simple; sm section
"Long arithmetic in factored form").
Catalan numbers
Catalan numbers - numerical sequence, which is found in a surprising number of combinatorial problems.
This sequence is named after Belgian mathematician Catalana (Catalan), who lived in the 19th century,
although in fact it was already known to Euler (Euler), who lived a century before Catalan.
Sequence
The first few Catalan numbers (Starting from zero):
Calculation
There are two formulas for the Catalan numbers: recurrent and analytical. Since we believe
that all the above problems are equivalent, to prove the formulas, we will choose the task with
which to do it the easiest way.
Recurrence formula
Recurrence formula is easily derived from the problem of the correct bracket sequence.
The leftmost opening parenthesis l corresponds to a certain closing bracket r, that breaks the formula two
parts, each of which in turn is correct bracketing sequence. Therefore, if we denote, for any fixed will be
exactly ways. Summing this over all admissible , We obtain the recurrence relation for .
An analytic formula
Decision
You can solve this problem using Lemma Burnside and Polya theorem. [Below is a copy of the text of this article] In this task, we
can immediately find a group invariant permutations. Obviously, it will consist of permutations:
Find an explicit formula for calculating . First, note that the permutations have the form that -
oh permutation on Second position is (Taken modulo If it is larger). If we
consider the cyclic structure th permutation, we see that the unit goes into , passes
And so on, until we arrive at the
in , - In number of ; for the remaining elements
performed similar allegations. From this we can understand that all cycles have the same length,
equal Ie ("Gcd" - the greatest common divisor, "lcm" - the least common
fold). Then the number of cycles in Second permutation is simply equal to .
Substituting these values in the Polya theorem, we obtain decision:
You can leave a formula in this form, but you can minimize it even more. We proceed from the sum of all to sum only divisors
. Indeed, in our sum will be many of the same terms: if is not a divisor , Then there is such a divisor after calculation.
Consequently, for each divisor of its
summand will take into account several times, i.e. the amount may be represented in the form:
whe
re - The number of such numbers That . Find an explicit expression for this quantity.
Any such number is: Wherein (Otherwise ).
Remembering Euler's function, We find that the number of such - is the value of the Euler function .
Thus, And finally obtain formula:
The alignment of elephants on a chessboard
Required to find the number of ways to arrange K elephants on board size NxN.
Algorithm
Will solve the problem using Dynamic Programming.
Let D [i] [j] - The number of ways to arrange j elephants on the diagonals to the i-th inclusive, and only
those diagonals that the same color as the i-th diagonal. Then, i = 1 .. 2N-1, j = 0 .. K.
Diagonal enumerate as follows (example for boards 5x5):
Black: White:
1 _ 5 _ 9 _ 2 _ 6 _
_ 5 _ 9 _ 2 _ 6 _ 8
5 _ 9 _ 7 _ 6 _ 8 _
_ 9 _ 7 _ 6 _ 8 _ 4
9 _ 7 _ 3 _ 8 _ 4 _
Ie odd numbers correspond to black diagonals, even - white; diagonal enumerate in order to
increase the number of elements in them.
With this numbering, we can calculate each D [i] [], based only on D [i-2] [] (deuce deducted that we
treat the diagonal of the same color).
So, let the dynamics of the current element - D [i] [j]. We have two transitions. First - D [i-2] [j], i.e. put all j elephants
previous diagonal. Second transition - if we put one elephant at the current diagonal, and the remaining j-1 elephants - previous;
note that the number of ways to put an elephant on the current diagonal equal to the number of cells in it minus the j-1, because
elephants standing in the previous diagonals will overlap part of the directions. So
manner, we have:
wherein cells (i) - number of cells lying in the i-th diagonal. For example, cells can be calculated as follows:
It remains to determine the dynamics of the base, there is no difficulty: D [i] [0] = 1, D [1] [1] = 1.
Finally, calculating the dynamics actually find answer to solve the problem. Iterate number i = 0 .. K
elephants standing on the black diagonals (number of the last black diagonal - 2N-1), respectively, Ki elephants put
the white diagonal (diagonal number of the last white - 2N-2), i.e. to add the response value D [2N-1] [i] * D [2N-2] [Ki].
Implementation
int ans = 0;
for (int i = 0; i <= k; + + i)
ans + = d [n * 2-1] [i] * d [n * 2-
2] [k-i]; cout << ans;
Right parenthesis sequence
Correct bracketing sequence is a string consisting only of characters "brackets" (often considered only parentheses, but
here will be considered and the general case of several types of brackets), where each closing parenthesis there
corresponding opening (with the same type).
Here we consider the classic problem parenthesis on the right of the sequence (hereinafter, for brevity, simply "sequence") for
checking the accuracy, the number of sequences, all sequences generated by finding the lexicographically next sequence
finding - the second sequence in a sorted list of all sequences, and, conversely, determination
sequence numbers. Each of the problems addressed in two cases - when parenthesis are allowed
only one type, and when several types.
Formula
The number of correct sequences bracketed with one type of brackets can be calculated as Catalan
number. Ie if there pairs of brackets (line length ), The amount is equal to:
Suppose now that there is not one, and types of brackets. Then each pair of brackets
independently of the others can take one of types, and therefore we obtain the following
formula:
Dynamic programming
On the other hand, this problem can be approached from the standpoint of Dynamic Programming. Let -
number of correct sequences of bracketed steam
parentheses. Note that in the first position will always stand opening bracket. It is clear that within this set of parentheses is
worth some proper bracket sequence; similarly, after the pair of brackets is also worth correct bracket sequence. Now to count,
move, how many pairs of parentheses
will stand inside the first pair, then, accordingly, pair of brackets will stand after the first
pair. Therefore, the formula for has the form:
Remaining learn to look for that same position of the first changes. For this we follow the line from right to left
and maintain a balance open and closed brackets (opening brace at the meeting will
reduce And when the cover - increase). If at any time we are on the opening parenthesis,
and the balance after the treatment of the symbol is greater than zero, then we have found the right-most position from
which we can begin to change the sequence (in fact, means that the left has not yet closed
bracket). Deliver to the current position closing parenthesis, and then the maximum possible number of
opening parenthesis, and then all remaining closing brackets - the answer is found.
If we viewed the entire row and have not found a suitable position, the current sequence - the
maximum, and there is no answer.
Implementation of the algorithm:
string s;
cin >> S;
int n = (int) s.length();
string ans = "No solution";
for (int i = n-1, Depth =0; i> =0; -
I) { if (s[i] == '(')
- Depth;
else
+ + Depth;
if (s[i] == '(' && Depth> 0) {
- Depth;
int open = (n-i-1 - Depth) / 2;
int close = n-i-1 - Open;
ans = s.substr(0, I) + ')' + String ('(', Open) + String
(')', Close);
break;
}
}
cout << Ans;
This formula is derived from the following considerations. First, we "forget" about the fact that there are several brackets
types, and just take the response . Now calculate how to change the response due to the
of presence
bracket types. We have uncertain positions, of which are
brackets, closing some of the previously discovered - hence, the type of brackets we can not vary. But all
the other brackets (there are pairs) can be of any type, so
the answer is multiplied by the power of this .
Suppose now allowed not one, but types parentheses. Then the solution algorithm is different from previous
only case that we should value is multiplied by the amount
To take into account that this residue could be any type of braces and a pair of brackets
This balance is only As brackets are closing
for opening brackets outside of this residue (and their types because we can not vary). Implementation
of the Java language for the case of two types of brackets - round and square:
Ie .
Because any labeled graph uniquely defined by its edges, then the number of labeled graphs with vertices is equal to:
a set of vertices is (One vertex - the top one - iterate is not necessary). The amount of
ways to build a connected component of the vertices, we already know how to count - it . After removal
this component of the graph, we are left with a
graph and peaks connected components, ie
we got a recursive relationship on which you can calculate values :
Subtotal get code like this:
From a performance standpoint, this algorithm is linear (on average) if K is not close to N (ie, if not satisfied that K = N - o (N)). It
suffices to prove that the comparison "a [i] <n-k + i +1" performed in the amount of Cn +1 k times, ie in (N
+1) / (N-K +1) times more than there are combinations of all N elements in K.
We prove this.
To prove recall the fact that the sequence G (N) Gray codes can be obtained as follows:
ie take a sequence of Gray codes for the N-1, is appended to the beginning of each mask 0, add to
the answer; then take a sequence of Gray codes for the N-1, invert it appends to the beginning of
each mask and add 1 to the answer.
Now we can produce the proof.
First we prove that the first and last mask will differ in exactly two bits. It is sufficient to note that the first mask will look NK
zeros and K units, and the last mask will look like: a unit then NK-1 zeros, then K-1 unit. It is easy to prove by induction on N,
using the above formula
Gray codes for the sequence.
Now we prove that any two adjacent code will differ in exactly two bits. To do this, we turn again to the formula for the
sequence of Gray codes. Suppose that within each of the halves (derived from G (N-1))
statement is true, we prove that it is true for the entire sequence. It suffices to prove that it is true in
"gluing" two halves of G (N-1), and it is easy to show, based on the fact that we know the first and last
elements of these halves.
We now present a naive implementation that works for 2N:
int gray_code (int n)
{return n ^ (n >>
1);
}
It should be noted that it is possible and in some ways a more efficient implementation, which will build all kinds of
combinations on the go, and thus work for O (Cnk n). On the other hand, the
implementation is a recursive function, and so for small n, she probably has a large hidden
constant than the previous one.
Proper implementation itself - is a direct follow formula:
This formula can be easily obtained from the above formula for the sequence of Gray - we just
choose a subsequence of elements suitable for us.
Burnside lemma
This lemma was formulated and proved Burnside (Burnside) in 1897, but it was found that this formula has been
previously opened Frobenius (Frobenius) in 1887, and even earlier - Cauchy (Cauchy) in 1845, therefore this formula is
sometimes called Burnside's lemma, and sometimes - the Cauchy-Frobenius theorem.
Burnside lemma allows to count the number of equivalence classes in a set based on some of its
internal symmetry.
Many objects here - it's a lot of different colorings in this understanding of trees.
We now define a set of representations. Each coloring we associate its defining feature where
And. Then the set of ideas - it's a lot of different
functions of this form, and its size is obviously equal to . At the same time, this set of
representations we have introduced a partition into equivalence classes.
For example, suppose And the tree is as follows: root - the top one, and the
vertices 2 and 3 - her sons. Then the following functions and to be equivalent:
Permutation invariant
Why are these two functions and belong to the same equivalence class? Intuitively, this is clear - because we can swap the
sons of vertex 1, ie peaks 2 and 3, after such a conversion function and coincide. But formally, this means that there exists
permutation invariant (I.e., that
according to the problem does not change the object itself, but only its representation), such that:
So, based on the conditions of the problem, we can find all permutation invariant, ie applying not that we do not go from
one equivalence class to another. Then, to check whether the two functions
equivalent (ie, whether they are actually the same object), it is necessary for each
permutation invariant check to see whether the conditions: (Or, equivalently,
). If at least one permutation found this equation, then the equivalent, otherwise they
not equivalent.
Finding all these permutations invariant with respect to which our problem is invariant - a key step for the application
of the lemma as Burnside and Polya theorem. It is clear that these invariant permutations depend on the specific
problem, and finding them - a process purely heuristic based on intuitive considerations. However, in most cases it is
sufficient to manually find a few "core" of permutations, from which all others can be obtained by permutation of all
possible products (and this, only the mechanical part of the work can be shifted to the computer, more detail is
discussed below for an example of the problem) .
It is easy to understand that the invariant permutations form group - As the product of any permutation
invariant is also invariant permutation. Denote group
permutation invariant through .
Although Burnside lemma itself is not so convenient to use in practice (it's unclear how quickly to seek value), it is most
clearly reveals the mathematical essence, which is based on the idea of counting
equivalence classes.
PROOF Burnside
Described here is the proof of Lemma Burnside is not so important for its understanding and application
in practice, so it can be omitted on first reading.
The proof given here is the easiest of the famous and not using group theory. This proof was published Bogart
(Bogart) and Kenneth (Kenneth) in 1991
The quantity on the right - it is nothing like the amount of "invariant pairs", ie pairs such that
. Obviously, in the formula, we have the right to change the order of summation - do outside sum
the elements f, and inside put value - The number of permutations, for which f is invariant:
To prove this formula up a table whose columns will be signed by all the values, row
- All the permutations, and in the cells of the table will stand works. Then, if we
consider the columns of the table as a set, some of them may coincide, and this will mean as that corresponding to each
column are also equivalent. Thus, as many different
set of columns is unknown quantities . Incidentally, from the point of view of group theory
column of the table, signed by some element - is the orbit of this element; for the equivalent
elements, obviously the same orbit, and by giving it different orbits .
So, the table columns themselves fall into equivalence classes; Now fix any class and consider the columns in it. First,
we note that in these columns can stand only elements of one
equivalence class (otherwise it would turn out that some equivalent transformation we transferred to another equivalence
class, which is impossible). Secondly, each item will meet the same number of
once all the columns (also from the fact that the columns correspond to equivalent elements). It can
be concluded that all the columns within the same equivalence class coincide with each other as a
multiset.
Now fix an arbitrary element. On the one hand, it is found in a column exactly once (by definition). On the other hand, all
the columns within the same equivalence class are identical
as a multiset. Hence, within each column of any part of the equivalence class element
occurs exactly times.
Thus, if we take an arbitrary manner from each equivalence class by one column
and sum the number of elements in them, we find on the one hand, (It
obtained simply by multiplying the number of columns on their size), and on the other hand - the sum of the
quantities all
(This follows from all the previous arguments):
QED.
Polya theorem. The simplest version of
Theorem Polya (Polya) is a generalization of Lemma Burnside, besides providing a convenient tool for finding number of equivalence
classes. It should be noted that even before the Polya theorem was discovered and proved Redfield (Redfield) in 1927, but its
publication was passed unnoticed by mathematicians of the time. Polya independently came to the same result only in 1937, and its
publication has been more successful.
Here we look at the formula obtained as a special case of Polya theorem, and which is very useful for computing practice.
Overall Polya theorem in this article will not be considered.
We denote the number of cycles in the permutation . Then we have the following formula
(special case of Polya)
where - the number of values that can be taken each element representation . For example, in
our problem-example (coloring the root of a binary tree in 2 colors) .
Proof
This formula is a direct consequence of Lemma Burnside. To get it, we just need to find an explicit expression for the
value in Lemma (recall that the number of fixed
points permutation ).
So, consider some permutation some element. Under the action of the permutation moving the elements, as is known, for
cycles permutation. Note that, since the result to be obtained
Then within each cycle permutation must be identical elements. At the same time, to
different cycles is no connection between the values of the elements does not occur. Thus, for each cycle permutation we
choose one value (of options), and thus we obtain a representation of all
Find an explicit formula for calculating . First, note that the permutations have the form that -
oh permutation on Second position is (Taken modulo If it is larger). If we
consider the cyclic structure th permutation, we see that the unit goes into , passes
And so on, until we arrive at the
in , - In number of ; for the remaining elements
performed similar allegations. From this we can understand that all cycles have the same length,
equal Ie ("Gcd" - the greatest common divisor, "lcm" - the least common
fold). Then the number of cycles in Second permutation is simply equal to .
Substituting these values in the Polya theorem, we obtain decision:
You can leave a formula in this form, but you can minimize it even more. We proceed from the sum of all to sum only divisors .
Indeed, in our sum will be many of the same terms: if is not a divisor , Then there is such a divisor after calculating .
whe
re - The number of such numbers That . Find an explicit expression for this quantity.
Any such number is: Wherein (Otherwise ).
Remembering Euler's function, We find that the number of such - is the value of the Euler function .
Thus, And finally obtain formula:
Wherein , , .
Thus, we can write the implementation of solving this problem:
int main() {
int n, m;
cin >> N >> m;
The wording
The principle of inclusion-exclusion is as follows:
To calculate the size of combining multiple sets, it is necessary to sum the sizes of these sets separately, then subtract
all sizes pairwise intersection of these sets, add
intersections of all possible sizes back triples sets, subtract the size of intersections
quadruplesAnd so on up to the intersection all sets.
It can be written in a more compact, a sum of over subsets. We denote set whose elements are . Then the
principle of inclusion-exclusion takes the form:
Then the area of association equal to the sum of the areas, and less double coated
areas , , , But with the addition of three covered surface :
● in terms of those who Element take into account exactly times with the sign of
;
● in terms of those who Element take into account the zero times.
Thus, we need to calculate a sum binomial coefficients:
The easiest way to calculate this amount by comparing it with the expansion of the binomial theorem in the
expression :
, As required.
problems that are difficult to solve without the use of the principle of inclusion-exclusion.
Of particular note is the problem of "search number of ways," because it demonstrates that the principle of inclusion-exclusion
Subtract this number from the total number of permutations , We get a response.
The dimensions of
each of the are obviously (Since such sequences may occur only two
kind of numbers.) Capacity of each pairwise intersection are equal (as is available only
one digit). Finally, the cardinality of the intersection of all three sets is (As available figures do
not remain).
Remembering that we solve the inverse problem, we obtain the final answer:
Forget about the first limitation And simply count the number of non-negative solutions of this equation.
This is easily done through binomial coefficients - We want to break elements on the groups, i.e.
distribute the "walls" separating groups, places:
Now count on inclusion-exclusion formula by "bad" decisions, ie solutions of these in which one or
more more .
We denote (Wherein ) The set of solutions of an equation in which And all
, We note that we have
other (All ). To calculate the size of the set essentially the
a combinatorial problem that is solved in two paragraphs above, only now items excluded from
consideration and exactly belong to the same group. Thus:
Similarly, the cardinality of the intersection of two sets and equal to the number:
The power of each of three or more sets is equal to zero, since elements is not enough for
three or more variables, more than or equal to .
Combining all this inclusion-exclusion formula and given that we solve the inverse
problem, we finally obtain answer:
Number of relatively prime numbers in a given interval
Let numbers and . Required to count the number of numbers in the interval Prime to . Go straight
to the inverse problem - do not count the number of relatively prime integers.
Consider all prime divisors ; denote them by ( ). How many numbers in the
However, if we simply sum up these numbers, we get the wrong answer - some numbers to be
added together several times (the ones that are divided to several ). Therefore it is
necessary to use inclusion-exclusion formula.
For example, you can iterate over a subset of all 's, Find them work, and add or subtract to the
inclusion-exclusion formula next term.
Summary implementation to count the number of relatively prime numbers:
int sum = 0;
for (int msk =1; msk <(1<< P.size()); + +
Msk) { int mult = 1,
bits = 0;
for (int i =0; i <(int)p.size(); +
+ I) if (msk & (1<< I)) {
+ + Bits;
mult * = p[i];
}
return r - sum;
}
Looking into Graham (Graham, Whip Patashnik. "Concrete Mathematics" [1998] ), We see a
well-known formula for binomial coefficients:
Applying it here, we see that the whole sum of the binomial coefficients is minimized in:
Thus, for this version of the problem we got the solution with the asymptotic :
Number of ways
There is a field Some cells which - impenetrable wall. In the field in the cell (Bottom left
cell) is initially robot. The robot can only move right or up, and eventually he should get
squared Avoiding all obstacles. Required to count the number of ways in which he can do it. Assume that the dimensions
and very large (say, up to ), And the amount of - Small (of the order ).
To solve immediately for convenience sort the obstacles in the order in which we can get around
them: ie, for example, the coordinate , And at equality - coordinate .
Also learn how to solve the problem immediately without obstacles: ie learn to count the number of ways to reach
from one cell to another. If one coordinate we need to go cells, and on the other - cells, from simple
combinatorics, we obtain a formula through binomial coefficients:
Now count the number of ways to reach from one cell to another, avoiding all obstacles, you can
use inclusion-exclusion formula: Count the number of ways of walking, stepping at least one
obstacle.
For this example, you can iterate over a subset of the obstacles which we do come, count the number of ways to
do it (just multiplying the number of ways to reach from the start to the first cell of the selected obstacles, the first
obstacle to the second, and so on), and then add or subtract a number from the response in accordance with the
standard inclusion-exclusion formula.
However, this again is a non-polynomial solution - for the asymptotic
behavior . We show how
get polynomial solution.
Will solve dynamic programming: Learn how to calculate the number - Number
ways to reach from the second point to the second, while not stepping on any one obstacle (except the and
of course). In total we will point as to the obstacles added start and end cells.
If we for a moment forget about all the obstacles and simply count the number of paths from cells in a cage, we thus take
into account some of the "bad" way through obstacles. Learn to count the number of these "bad"
tract. Iterate over the first obstacle To which we come, then the number of paths is
equally Multiplied by the number of arbitrary ways in . Summing it all, we
count the number of "bad" ways.
. Therefore, the solution of the whole
Thus, the value of we have learned to count during problem
has the asymptotic .
where - Is the number of prime number factorization , - The number of fours, dividers for .
To calculate the function, you simply count the number of numbers divisible And binomial
coefficient count the number of ways to choose four of them.
Thus, using the inclusion-exclusion formula, we summarize the number of fours divisible by primes,
then subtract the number of quads that are divisible by the product of two primes, we add four
divisible by three simple, etc.
Therefore we need a faster solution that calculates the answers to all the numbers in the interval
immediately. This can implement such modification of the sieve of Eratosthenes:
● First, we need to find all the numbers in the interval in which no prime factorization is not included
twice. In addition, for inclusion-exclusion formula, we need to know how simple factorization contains
every such number.
To do this we need to have arrays that store for each number the number of primes in its factorization, and - Containing for
each number or - All simple enter it in power or not.
Thereafter, during the sieve of Eratosthenes in processing the next prime number, we'll go over all
multiple of the current number, and increase them, and all the numbers divisible by the square of the current simple
- Put .
● Secondly, we need to calculate the answer for all the numbers from to Ie array - The number of
numbers that are not
relatively prime to the data.
For this, we recall how the inclusion-exclusion formula - here we actually implement it the same, but
with an inverted logic: if we iterate term and see in which inclusion-exclusion formula for what number
this term included.
So, suppose we have a number for which Ie this number is participating in formula
inclusion-exclusion. Iterate through all the numbers that are And to
multiples answer Each of these properties, we have
add or subtract value . Zodiac - addition or subtraction - depends on If
odd, then we must add, subtract otherwise.
Implementation:
int n;
bool good[MAXN];
int deg[MAXN], Cnt[MAXN];
(Moreover, if the expression is rounded to the nearest whole - you get exactly the number of
permutations with no fixed points)
We denote set of permutations of length with a fixed point at the position ( ). We now use the
inclusion-exclusion formula to calculate the number of permutations with at least
one fixed point. To do this, we need to learn to count-size sets of intersections They
as follows:
because if we know that the number of fixed points is , We thus know the position permutation
Element set equal Obtain a formula for the number of permutations with at least one fixed point:
Simplifying this expression, we obtain exact and approximate expressions for the
number of permutations with no fixed points:
(Since the sum in brackets - this is the first members of the Taylor series expansion )
In conclusion, it is worth noting that in a similar way to solve the problem when you want to fixed points was not among
the first elements of the permutation (but not among all, as we have just solved). Will result in the formula as the above exact
formula, only with the amount will go up And not to .
search and update can be greatly accelerated, bringing to a linear asymptotic behavior.
Iterate through all the vertices of which are initially known to winning or losing. From each of them let the next dfs. This dfs will move
in reverse edges. Above all, he will not come to the top, which is defined as winning or losing. Further, if dfs tries to go from losing the
top in some vertex, it marks it as winning, and goes into it. If dfs tries to go from winning the top in some vertex, it must verify that all
edges are from this vertex in winning. This test is easy to implement in O (1) if at each vertex will store counter edges that lead to
winning the top. So, if dfs tries to go from winning the top in some vertex, then it increases it count, and if the counter equalizer
with the number of edges emanating from that vertex, then this vertex is marked as losing, and depth-first search is
в this vertex. Otherwise, if the target vertex and is not defined as winning or losing, the search
в depth does not enter into it.
Overall, we find that each winning and losing each vertex is visited by our algorithm exactly once, and no man tops and not
visited. Consequently, the asymptotic behavior really O (M).
Implementation
Consider the implementation of depth-first search, on the assumption that the graph is built in memory of the game,
raising the outcome counted and recorded in degree (this is just a counter, it will decrease if there is an edge in
winning the top), as well as winning or losing initially vertices already labeled.
vector <int> g
[100]; bool win
[100];
bool loose [100];
bool used [100];
int degree [100];
void dfs (int v) {used
[v] = true;
for (vector <int> :: iterator i = g [v]. begin (); i! = g [v]. end
(); + + i) if (! used [* i]) {
if (loose [v])
win [* i] = true;
else if (- degree [* i] ==
0)
loose [* i] = true;
else
continue;
dfs (* i);
}
}
Example task. "Police and Thief"
To algorithm become more clear, we consider it a specific example.
Condition of the problem. There is a field size MxN cells in some cells can not go. Known initial coordinates
police and thief. Also on the card may be present output. If a police officer would be in the same cage with the
thief, the police won. If the thief would be in the cell with the output (in this cell should not police), will win a
thief. Cop can walk in 8 directions, the thief - only 4 (along the coordinate axes). And the policeman and the
thief may pass their turn. The first move is made by a police officer.
Graphing. Construct a graph of the game. We need to formalize the rules of the game. Current
state game defined by the coordinates of police P, thief T, as well as boolean Pstep, which
determines who will make the next move. Consequently, the vertex of the graph is defined triple (P,
T, Pstep). Count to build easily by simply matching condition.
Next you need to determine which vertices are won or as lost initially. There are delicate moment.
Winning / losing vertex coordinates in addition depends on the Pstep - whose now course. If you
now move the police, then the top of winning, if the coordinates of a policeman and the thief are the
same; top of losing if it is not successful and the thief is on the way. If the thief's turn, the top
winning, if the thief is on the way, and losing, if it is not successful and coordinate police and thief
are the same.
The only time that you need to decide - to build graph explicitly or do it "on the run"Right in the
DFS. On the one hand, if you build a graph in advance, it will be less likely to make a mistake. On
the other hand, it will increase the amount of code, and the work will be several times slower than
if you build a graph" on the fly " .
Implementation the entire program:
struct state
{char
p, t;
bool
pstep
;
};
int n, m;
cin >> n >> m;
vector <string> a
(n); for (int i =
0; i <n; + + i)
cin >> a [i];
Introduction
Theory Shpraga Grande - a theory that describes the so-called equal (Eng. "impartial") play two players, ie games in
which the permitted moves and winning / losing only depend on the state of the game. Matter which of the two players
go, does not depend on anything: ie players are completely equal.
In addition, it is assumed that the players have all the information (about the rules of the game,
the possible moves, the position of opponent).
It is assumed that the game finiteIe for any strategy players will sooner or later come to losing position from which there
are no transitions to other positions. This position is for a losing
player who must make a move from this position. Accordingly, it is advantageous for a player who came
into this position. Clearly, a draw in this game does not happen.
In other words, this game can be completely described directed acyclic Count: vertices in it are the state of the game,
and ribs - Transitions from one state to another game as a result of the progress of the current player (again, in the first
and second player are equal). One or more
vertices have no outgoing edges, it is as lost vertices (for a player who has to make the course of such
vertices).
Since a tie does not happen, then all the states of the game are divided into two classes: winning and losing. Winning -
these are status that there is a move of the current player, which will
to inevitable defeat another player even if his best game. Accordingly, losing the state - a state from which all
transitions lead to states, leading to the victory of the second player, in spite of the "resistance" to the first player. In
other words, winning is a state from which there is at least one transition in a losing state, and losing is a condition
from which all transitions lead to winning the state (or from which there are no transitions).
Our task - for any given game to classify the states of this game, ie for each state to
determine winning or losing it.
Theory of games independently developed Shprag Roland (Roland Sprague) in 1935 and Patrick
Michael Grundy (Patrick Michael Grundy) in 1939
Game "Him"
This game is one of the examples described above games. Moreover, as we shall see later, any Games of
equal two players actually equivalent to the game "it" (Eng. "nim"), so the study of this game will allow us to
automatically solve all the rest of the game (but more on that later).
Historically, this game was popular in ancient times. Probably, the game has its origins in China - at least, the
Chinese game "Jianshizi" is very similar to him. In Europe, the first mention of Nimes refer to the XVI century. The
name "it" came up with mathematician Charles Bud (Charles Bouton), which in 1901 published a full analysis of this
game. Origin of the name "him" is not known.
Game Description
Game "it" is a next game.
There are several piles, in which each of several stones. In one move, the player can take any one of a
handful of any non-zero number of stones and throw them away. Accordingly, the loss occurs when there
are no more moves, ie all the piles are empty.
So, the state of the game, "it" is uniquely described by an unordered set of natural numbers. In one move allowed strictly to
reduce any of the numbers (if the resulting number will be zero, it is removed from the set).
Solution of neem
The solution to this game published in 1901 by Charles Bud (Charles L. Bouton), and it looks as follows.
Theorem. The current player has a winning strategy if and only if when XOR-sum of the sizes of heaps different from
zero. Otherwise, the current player is in a losing position. (XOR-sum of the numbers
is an expression Wherein - Bitwise exclusive or)
Proof.
The main essence of the proof below - available symmetric strategy
for the enemy. We show that being in a state of zero XOR-sum the player can not get out of this state
- in any of its transition to a state with nonzero XOR-sum of the enemy there is a retaliatory move that
returns the XOR-sum back to zero.
We now proceed to the formal proof (it will be constructive, ie, we show how it looks symmetrical strategy
opponent - exactly what you will need to move him to perform).
However, since it means that. Hence, the new state will have a nonzero XOR-sum, ie, according to the
basis of induction is advantageous, as required.
● Let. Then our task - to prove that the current state - a winner, ie of course it exists in a losing state (zero XOR-sum).
Consider the bit representation of the number . Take senior nonzero bit, let his room number. Let - the number of the
heap, in size which th bit different from zero (there is, otherwise in XOR-sum This bit
would not have been different from zero).
Then, allegedly sought move - change it Th pile, making its size . Let us
prove this.
You should first check that it is the correct move, ie that . However, it is true, as all the bits, the senior
Th, y and coincide, and Th bit in will be zero, while the will be one.
Now calculate what XOR-sum give the course:
Thus, we specify the course - really move in a losing state, and this proves that the current state
winner.
The theorem is proved.
Consequence. Any condition them games can be replaced by an equivalent condition consisting of only a
handful of size equal to the XOR-sum of the sizes of piles in the old state.
In other words, the analysis of neem with several small groups can calculate the amount of XOR-
their size, and proceed to the analysis of only a handful of neem size - As shown by the theorem
just proved, winning / losing will not change.
where the function from a set of numbers returns the smallest non-negative number is not found
in this set (the name "mex" - an abbreviation of "minimum excludant").
Thus, we can, starting from the vertex without outgoing edges, gradually calculate values Shpraga Grande
for all the states of our game. If the value of what Shpraga Grande-
or state is zero, then this condition is disadvantageous otherwise -
For the vertices of which there is no transition, the value of according to the theorem will be obtained as
from the empty set, ie . But, in fact, the state without transitions - is losing condition, and he
really should correspond to the size of a bunch of them- .
Consider now any state From which there are transitions. By induction, we can assume that for all states In which we
can move from its current state, the values already counted.
Thus, in comparison with Theorem Shpraga Grande here we take into account the fact that the game
can be transitions from individual states in the sum of several games. To work with the sums of
games, we will first replace every game its value Grande, ie a bunch of them-some size. After that we
come to him, the sum of several piles, ie nimu to normal, the answer to which, according to Theorem
Bouton - XOR-sum of the sizes of heaps.
Examples of games
To demonstrate the theory Shpraga Grande, we explain a few problems.
Especially should pay attention to the problem "stair him", "nimble-2", "turning turtles", which shows
a non-trivial reduction of the original problem nimu with increases.
"Tic Tic"
Condition. Consider plaid stripes size cells. In one move, the player has to put one cross, but it
is forbidden to put two cross near (to neighboring cells). The player who can not make a move. To say
who will win the game at the optimum.
Decision. When a player puts a cross in any cell, we can assume that the whole band splits into two independent
halves: the left of the cross and to the right. At the same time the cell itself with a dagger, and its left and right
neighbor destroyed - because they can not be anything else to put.
Consequently, if we number the cells from strips to , Then put a cross in position ,
Band splits into two strips of length and Ie we move into the sum of two games
and . If the cross is put in the position or , It is a special case - we just move
on to the state .
"Pawns"
Condition. There is a field In which the first and third row are on pawns - white and black,
respectively. The first player goes white pawns, the second - black. Terms of stroke and stroke - standard chess,
except that beat (subject to availability) is obligatory.
Decision. Trace, what happens when one pawn to make a move forward. The next move opponent will be obliged to eat
it, then we would be required to eat a pawn of the opponent, then he will eat, and finally, our enemy pawn pawn eat and
stay, "resting" in the pawn of the opponent. Thus, if we go to the beginning column pawn
and . and
.
Condition.
Decision.
Condition. Yes
Decision. -
It is easy to obtain an expression for the function Shpraga Grande:
Calculate the table for it for the first few tens of elements:
You may notice that, at some point, the sequence becomes periodic with period . In the future, this
periodicity is also not violated.
Grundy's game
Condition. Yes heaps of stones, the dimensions of which we denote by . In one move, the player can take any pile
of size at least and divide it into two nonempty piles of unequal sizes. The player who can not move (ie, when the size
of the remaining piles is less than or equal to two).
Decision. If , All these multiple piles are obviously - independent games. Therefore, our task
- Learn how to look for a function Shpraga Grande for one heap, and the answer for a few piles will be obtained as their
XOR-sum.
For a handful of this function is constructed as easy enough to view all possible transitions:
What is this game is interesting - the fact that so far it has been found for the general law. Despite
the assumption that the sequence must be periodic, it was calculated up until
and frames in this region were detected.
"Stair him"
Condition. There is a staircase with steps (numbered from to ) On The second step is coins. In one
move is allowed to move a non-zero number of coins The second to Th step. The player who can not
make progress.
Decision. If you try to reduce this problem to nimu "Head" it turns out that the course we have -
this decrease a certain number of piles on, and a handful of other simultaneous increase at the
same rate. As a result, we obtain a modification of the neem, which is very difficult to solve.
Proceed differently: consider only the odd-numbered steps: . We will see how to
change this set of numbers when making a turn.
If the move is made with an even , Then this means an increase of stroke . If the move is made
with an odd , It means decrease .
It turns out that our problem - it is common with increases with the size of heaps .
Consequently, the function Grande from him - is XOR-sum of numbers of the form .
Decision "turning turtles". Allegedly this game - it is a regular on the numbers , where - position On
Noughts (1-indexed). Verify this assertion.
● If a player simply reversed toe on the cross, without using an additional course - it can be understood as the fact that he just took the
whole pile, corresponding to this crosses. In other words, if a player changed his toe on the cross at the position
, By the same token, he took a handful of size and made her size zero.
● If a player takes an extra swing, ie besides the fact that he changed positions in the cross to toe, he
Change your position in the cell , We can assume that it reduced pile to size .
Indeed, if the position of used to be a cross - that, in fact, after the player's turn there will be a toe, ie
bunch size is displayed . And if the position of used to toe, after the player's turn this bunch
disappears - or, what is the same, there was a second bunch of exactly the same size (As in Nimes two
piles of equal size actually "kill" each other).
Thus, the answer to the problem - it is XOR-sum numbers - coordinates all zeros in the 1-indexed.
Decision "twins". All the arguments, above, remain true, except that progress "Zero heap" is now a
player does not. Ie if we take away from all the coordinates unit - then again, the game will turn him
into a regular.
Thus, the answer to the problem - it is XOR-sum numbers - coordinates all zeros in the 0-indexed.
Northcott's game
Condition. There is a board resolution : rows and columns. On each line are two chips: one
black and one white. In one move, the player can take any piece of his color and move it inside the line to
the right or to the left by an arbitrary number of steps, but without jumping over another chip (and not
getting it). The player who can not make progress.
Decision. First clear each of lines form an independent board game. Therefore, the task is to analyze the game in a row,
and the answer to the problem is the sum of the XOR-Shpraga Grande for each of the rows.
Solving the problem for a single line, denoted by the distance between the black and white chip (which can vary
from zero to ). In one move, each player can either reduce for some arbitrary value, or possibly
increase it to a certain value (increase are not always available). Thus, this game - it "Him with increases"And,
as we already know, the increase in this game are useless. Consequently, the function Grande for one line - this is
the distance .
(It should be noted that such an argument is formally incomplete - as in "with increases in Nimes" assumed that the
game finiteAnd here are the rules of the game allow players to play indefinitely. However, an endless game can
not take place at the optimal game - because one player stands to increase the distance (Price approaching the
edge of the field), the other player near him, reducing back. Consequently, for optimal game opponent player will
not be able to make moves increase indefinitely, so still described the solution remains in force.)
Triomino
Condition. Given the checkered field size . In one move, a player can bet on the one figure in
the shape of the letter "G" (ie, the shape of the three connected cells that do not lie on the same line).
Forbidden to put a figure so that it crossed at least one cell with one of the previous set of figures. The
player who can not make a move.
Decision. Note that the formulation of a figure breaks the whole field into two separate fields. Thus, we need to analyze not only the
rectangular field, but the field in which the left and / or right edges are uneven.
Drawing a different configuration, you can see that whatever the configuration of the field, the main
thing - just how many cells in this field. In fact, if the current field free cells, and we want to break this
field into two fields size and (Where), it is always possible, i.e. you can always find an appropriate
place for figurines.
Thus, our task becomes such: initially we have a bunch of stones size And in one move
we can throw a handful of stone and then beat this pile into two piles of arbitrary size. Grundy
function for this game is:
Chips on a graph
Condition. Given a directed acyclic graph. Some vertices of the graph are the chips. In one move the
player can take any piece and move it along any edge in the new vertex. The player who can not make
a move.
It can also happen, and the second version of this problem when it is considered that if the two
pieces come in one vertex, then they both cancel each other out.
Solving the problem of the first embodiment. First all the chips - independent from each other,
so Our task - to learn to look Grundy function for one chip in the graph.
Given that the graph is acyclic, we can do it recursively: suppose we thought Grundy function for all descendants of the current node.
Then the function Grande in the current top - it from this set of numbers.
Thus, the solution of the problem is the following: for each vertex count Grundy function recursively if it was a
feature in this top. After that, the answer to the problem is the sum of the XOR-Grande from those vertices of
the graph, which by hypothesis are chips.
Solution of the second variant of the problem. In fact, the second version of the problem is no different
first. In fact, if the two pieces are in the same vertex of the graph, the resulting XOR-sum of their values Grande cancel
each other out. Hence, in fact it is the same problem.
Implementation
From the perspective of implementation of interest may be the implementation of
the function.
If this is not the bottleneck in the program, you can write some simple option for (Wherein
- The number of arguments)
int result;
for (int i =0; ; + + I)
if (! Used[i])
{
result = i;
2break;
}
return result;
}
Another option - use the technique "Numerical used ". Ie do not array
Boolean variables and numbers ("versions"), and make a global variable indicating the current version
number.
we increase the number of the current version, in the first cycle, we shall appear in the
Upon entering function array
And the current version number. Finally, in the second cycle, we simply compare with the number of
not the the current
version - if they do not match, it means that the current
number is not met in the array . The third cycle
(which previously vanish array ) In such a
solution is not necessary.
"Him giveaway"
That it that we considered throughout this article - also called "normal Nimes" ("normal nim"). In contrast, there is
also "These giveaway" ("Misère nim") - when a player
has made the last move loses (and not winning).
(Incidentally, appear to him like the board game - it is more popular in the version of "giveaway",
rather than "normal" version)
Decision such neem surprisingly simple: will act in the same way as in the usual Nimes (I.e., count XOR-sum of all
sizes of heaps, and if it is zero, then we lose with any strategy, and otherwise - to win by finding the transition to the
position with zero Shpraga Grande). But there is one exceptionIf
sizes of all piles are unity, the winning / losing swapped compared with conventional Nimes. Thus, the winning /
● [Difficulty: Easy]
● [Difficulty: Medium]
Learn how to solve this problem in the case where all linear, ie have the form:
where - Non-negative integers. Note that in these linear features free term is zero, because otherwise to answer immediately possible
to add this intercept, and solve the problem with zero constant term.
We fix a schedule - permutation . We fix some number And let the permutation is a
permutation , Which traded the first and th elements. Let's see how much has changed in this penalty:
easy to understand that changes have occurred only with Th and Th terms:
Clearly, if the schedule is optimal, then any change leads to an increase in the fine (or retain the previous value), so
the optimal plan can write the condition:
Rearranging, we obtain:
Thus, optimal schedule can be obtained by simply sorting all details with respect to in
reverse order.
It should be noted that this algorithm we got the so-called commutation technique:
we tried to swap the position of two adjacent elements of the schedule figured out how at this fine
changed and hence derived algorithm for finding the optimal schedule.
In this case it is assumed that all coincide with some function Which is increasing. Clearly, in this case optimally
Theorem Livshits-Kladova
Theorem Livshits-Kladova establishes that commutes reception is only applicable for the above
three special cases, and only them, ie:
Whe
● Linear case: rein - Non-negative constants
● Exponential case: Where - are positive constants,
Wher
● Identical case: ein - Increasing function.
This theorem was proved under the assumption that the penalty function are sufficiently
smooth (there are third derivatives).
In all three cases apply permutation technique whereby the desired optimal schedule can be found by simple
sorting, therefore, for the time .
Johnson's problem with two machines
There is and two machine parts. Every detail must first be processed on the first machine, and then - on the
second. In this Th item is processed on the first machine for time, and the second - for the time. Each tool at
each time point can only operate on one part.
Required to make such an order on the part supply machines to the final processing time of all the
details would be minimal.
This problem is sometimes called the problem of dual-processor maintenance tasks, or task Johnson
(named after SM Johnson, who in 1954 proposed an algorithm to solve it).
It is worth noting that when the number of tools greater than two, the task is NP-complete (as
demonstrated by Gary (Garey) 1976).
Construction of an algorithm
Note first that we can assume that the order of machining the first and second machines must be the same. In
fact, because details for the second machine are available Only after the first treatment, and when there are several
available for the second time their machine parts
processing is equal to the sum of their independently of their order - that is most advantageous to
send a second machine that of the details that has been treated before others on the first machine.
Consider the procedure for submission of details on machines, which coincides with their input order: .
We denote downtime the second machine directly before processing Second Part (after treatment
Second Part). Our goal - minimize the total simple:
For the second - because it becomes ready to be sent to the machine in a second time And second
Machine released at time , We have:
The third item becomes available at the time of the second machine And the machine is released
in So:
where
(As it can be seen by induction, either sequentially finding expression for the sum of the first two, three, etc. .)
We now use commutation technique: Try to swap any two adjacent elements
and and see how this will change in the total simple.
By type of function expressions for clear that the only change and ; denote their new values through
and .
Thus, the item to went to details Sufficient (but not necessary) that:
(Ie, we ignored the rest, is not changed, the arguments in the expression for the maximum, thus obtaining a sufficient but
not necessary condition for the old less than or equal to the new values)
Thus, we have comparator: Sorting out the details on it, we, according to the above calculations,
we obtain the optimal order of parts in which it is impossible to interchange any two parts, improving
the final time.
However, one can further simplify sorting, if you look at this the other comparator
side. In fact, he tells us that if a minimum of four numbers achieved
on an element of the array , The corresponding item should go ahead, and if the element of the array - then later. Thus
we obtain another form of the algorithm: sort the items of the minimum, and if
at least equal to the current part , Then this item should be processed first of the remaining,
otherwise - the last remaining.
Anyway, it turns out that the problem with two machines Johnson comes to sorting items with certain elements of the
Implementation
Implement the second option the algorithm described above, when the items are sorted by a minimum of And
then go to the beginning or the end of the current list.
struct item {
int a, b, id;
bool operator < (item p) const {
return min(a, b) <Min(p.a, p.b);
}
};
Here all the details are stored in the form of structures , Each of which contains values and original part number.
Details are sorted, then distributed to lists (These are the details that were sent to the queue), and (those that were sent to the
end). Thereafter, the two lists are combined (wherein the second list is taken in
reverse order), and then found the order of calculated required minimum time supported two variables and - The
liberation of the first and second machine, respectively.
Literature
● S.M. Johnson. Optimal two-and three-stage production schedules with setup
times included [1954]
● M.R. Garey. The Complexity of Flowshop and Jobshop Scheduling [1976]
Optimal selection of jobs in certain
completion times and durations of
execution
Suppose we are given a set of tasks, each task is known point in time to which this task to complete, and the duration of this job.
The process of performing a task can not be interrupted before completion. Required to make a schedule to perform the greatest
number of jobs.
Decision
An algorithm for solving - greedy (Greedy). Sort all tasks by their deadline, and we will consider them one by
one in order of deadline. We create all In which we will gradually put the job from the queue and retrieve the
job with the smallest execution time (for example, you can use the set or priority_queue). Initially empty.
Suppose we consider Th job. Please put it in . Consider the time interval between the deadline for completion On the
job and for completion On the job - a segment of a certain length .
Will be removed from the job (in increasing order of the remaining time of their implementation) and put on a
performance in this segment, until it fills the entire segment . Important point - if at some time next extracted
from the structure of the job can be done in time to partially segment We perform this task partially - is as far
as possible, i.e. for time units, and the remainder of the job is placed back in .
At the end of this algorithm, we will choose the optimal solution (or at least one of several
solutions). Asymptotics of solutions - .
Implementation
int n;
vector <pair <int,int>> A; / / assignment as pairs (Deadline duration)
N reading ... and a ...
Solution for
Try to find a pattern that expresses the answer to the problem through the preceding problem.
Here 1-indexing formula spoils elegance if numbered positions from scratch, you get a very apt
formula:
Nonrecursive form:
Also need to separately analyze the case when will be less - in this case the above
optimization degenerate into an infinite loop.
Implementation (For convenience 0-indexed)
using the expansion of the logarithm in a Taylor series, we obtain a rough estimate:
obtained from the response to multiplying by two and subtracting one (due to the shift position):
Similarly, in case of odd will be struck by all the even number, then the first number, and will
challenge for And taking into account second shift positions obtain formula:
When implementing this can be used directly recursive dependency. This pattern can be transformed into
another form: represent the sequence of odd
Numbers "restarts" a unit whenever is a power of two. This can be written in the form of a formula:
Analytical solution for
Despite the simple form of the problem and a large number of articles on this and related problems, a simple
analytic representation of the solution of the problem of Joseph is still not found. For smaller derived some formulas,
but, apparently, they are hardly usable in practice (for example, see Halbeisen, Hungerbuhler "The Josephus
Problem" and Odlyzko, Wilf "Functional iteration and the Josephus problem").
Game Fifteen: the existence of solutions
Recall that the game is a field on On which are arranged chips, numbered from to And one field is left blank.
Required at each step moving any chip on the loose
position in the end come to the following positions:
Fifteen game ("15 puzzle") was invented in 1880 Noyes Chapman (Noyes Chapman).
Existence of solutions
Here we consider the following problem: for a given position on the board to say whether there
is a sequence of moves leading to a decision or not.
Let some position on the board:
(Ie a permutation corresponding position on the board, without the zero element)
We denote the number of inversions in the permutation (ie, the number of such elements and that ,
but ).
Further, let - The line number in which there is an empty element (ie,
in our notation .
Then, solution exists if and only if when even.
Implementation
We illustrate the above algorithm using code:
int a[16];
for (int i =0; i <16; +
+ I) cin >>
A[i];
int inv = 0;
for (int i =0; i + +
<16; I)
if (a[i]) (int j =0; j <i; + +
for J)
if (a[j] > A[i])
for (int i =0; i + + + + Inv;
<16; I)
if (a[i] == 0)
+ =
inv 1 + I / 4;
puts ((inv & 1) ? "No Solution" : "Solution Exists");
Proof
Johnson (Johnson) in 1879 proved that if odd, then there is no solution, and Storey (Story) in the same
he proved that all items for which even have a solution.
However, both of these proofs were quite complex.
In 1999, Archer (Archer) suggested a much simpler proof (article can download it here).
Stern-Brocot tree. Farey series
Stern-Brocot tree
Stern-Brocot tree - it's elegant design, allowing you to build the set of all non-negative fractions. It was independently
discovered by the German mathematician Maurice Stern (Moritz Stern) in 1858 and
French watchmaker Achilles Brokaw (Achille Brocot) in 1861, however, according to some sources, this
design has been opened ancient Greek scholar Eratosthenes (Eratosthenes).
On zero iteration we have two fractions:
Then, each follow iteration takes this list and fractions between neighboring fractions
The second:
On the third:
Continuing this process to infinityIs approved, you can get a lot of all non-negative fractions. Moreover, all the fractions
are obtained different (I.e., the current set of each fraction occurs not more than once), irreducible (Numerators and
denominators will be obtained relatively prime). Finally, all fractions will be automatically ordered ascending. The proof of
all these remarkable properties of wood-Stern Brokaw will be described below.
It remains only to bring the image of the tree Stern-Brokaw (as we have described it with the changing of the set). At the
root of this tree is infinite fraction And the left and right of the tree
and fractions are. Any tree node has two sons, each of which is obtained as the mediant of its left
and right ancestor ancestor:
Proof
Order. It proved very simple: note that the mediant of two fractions is always
between them, i.e.:
provided that
This can be proved simply by bringing the three fractions to a common denominator.
Since zero-order iteration took place, it will be stored and each new iteration.
Irreducibility. For this show, that at each iteration, for any two adjacent fractions in the list and
performed:
However, the truth of these conditions is obvious, provided the truth . Thus, actually,
this property holds at the current iteration, as required.
The presence of all fractions. The proof of this property is closely related to the algorithm of finding the fraction in Stern-
Brocot tree. Given that the Stern-Brocot tree all fractions in order, we find that for every vertex of the tree in the left subtree
are a fraction smaller than her, and the right - its great. Hence we get the obvious and search algorithm in a fraction of
Stern-Brocot tree: first, we are at the root; compare our fraction and a fraction recorded in the current node, if the fraction is
less than ours, then go to the left subtree if our shot more - go to the right, and if the same - found shot, the search is
completed.
To prove that an infinite tree Stern-Brokaw contains all the fractions, it suffices to show that this search algorithm fraction
completed in a finite number of steps for any given shot. This algorithm can be
understood as follows: we have the current
segment In which we are looking for our shot. Initially , .
At each step, a fraction compared with MEDIANT endpoints, ie with , And depending on this, we
or stop the search, or go to the left or right part of the segment. If the search algorithm fraction worked indefinitely, the
following conditions have been fulfilled for each iteration:
(Used here is that they are integral, and therefore from should ) Then, multiplying
the first to And the second - on the And adding them, we obtain:
The brackets on the left and the fact that (See the proof of the previous properties)
we finally obtain:
Since at each iteration is at least one of the variables strictly increasing, the process of finding fractions
will not contain more than iterations, as required.
An algorithm for constructing the tree
To build any subtree of the Stern-Brokaw enough to know only the left and right ancestors. Initially, at the first level, is the
ancestor of the left and the right -. They can be used to calculate the fraction in
current node, and then start from the left and right sons (left son passing itself as an ancestor of the
right and the right son - as a left ancestor).
Pseudocode of this procedure, all trying to build an infinite tree:
Irrational number in a Stern-Brokaw will meet endless sequence of characters; if you know any preassigned accuracy, we
can restrict some prefix of this infinite sequence. During this endless search irrational fractions in Stern-tree algorithm
Brokaw each time will find a simple fraction (with gradually increasing denominators), which provides a better
approximation of irrational numbers (just use this important time in the art and therefore Achilles Brokaw and discovered
this tree).
Farey sequences
Farey sequence of order is the set of all irreducible fractions between 0 and 1 whose denominators do not
exceed , And fractions are arranged in ascending order.
This sequence is named after the English geologist John Farey (John Farey), who tried in 1816 to prove that in any
number of Farey mediant fraction is two adjacent. It is known that it
proof was wrong and correct proof offered later Cauchy (Cauchy). However, back in 1802 mathematician Haros (Haros) in one
of his works came to almost the same results.
Farey sequence and have their own set of interesting properties, but the most obvious of their connection with the
Stern-Brocot tree: Actually, Farey sequence is obtained by removing some branches of the tree. Or we can say that for
Farey sequence need
take the set of fractions obtained in the construction of wood-Stern Brokaw on infinite iteration, and leave this set only fractions
with denominators not exceeding and numerators, denominators not exceeding.
Of the algorithm for constructing a tree-Stern Brokaw should and similar algorithm for Farey sequences.
At zero iteration include in the set and only shot. At each subsequent iteration we between each
two neighboring fractions insert their mediant, if the denominator does not exceed . Sooner or
later cease to be in the set of changes, and the process can be stopped - we found the required
sequence Farey.
We compute length Farey sequence. Farey sequence of order contains all
Farey sequence of elements of order And all irreducible fractions with denominators equal
But this quantity is known, power . Thus, the length Farey sequence of order
expressed by the formula:
or revealing recursion:
Literature
● Ronald Graham, Donald Knuth, and Oren Patashnik. Concrete Mathematics. Base
Informatics [1998]
Search subsegments array with a
maximum / minimum amount
Here we consider the problem of finding subsegments array with a maximum amount ("maximum subarray problem"
in English), as well as some of its variations (including a variant of this algorithm for solving the problem in the online
- A self-described algorithm - KADR (Yaroslav Tverdohleb)).
For example, if all the numbers of the array would be non-negative, the response could take the entire array. Nontrivial
solution when the array can contain both positive and negative numbers.
It is clear that the problem of finding minimum subsegments - essentially the same, you just change
the signs of all the numbers on the opposite.
Algorithm 1
Here we consider the almost obvious algorithm. (Next we consider another algorithm, which is
slightly more difficult to come up with, but its implementation is obtained even shorter.)
For convenience we introduce designation:. Ie array - An array of partial sums array . Also
set value .
We now sort out index And learn the current value for each quickly find the optimum , At which
Implementation
For implementation we do not even need to explicitly store an array of partial sums - from it we will
required only the current item.
The implementation is given in 0-indexed arrays, but not in 1-numbering as described above. We first present a solution
that is simple numerical answer without finding the desired segment codes:
if (sum <min_sum) {
min_sum = sum;
min_pos = r;
}
}
Algorithm 2
Here we consider a different algorithm. Its a bit more complicated to understand, but it is more elegant than the above, and
realized just a little shorter. This algorithm was proposed by Jay Kadan (Jay Kadane) in 1984
,
But, in fact, consider an arbitrary interval Wherein is not in a "critical" positions (ie
is Where - the last such position, in which ). Since the last critical
position is strictly earlier than It turns out that the sum of nonnegative.
This means that the moving position We will increase the response or, in extreme cases, do not change it.
Anyway, but it turns out that when you search for the answer really can restrict the segments, starting immediately after
the position in which exerted . This proves the correctness of the algorithm.
Implementation
As in Algorithm 1, we present first a simplified implementation that searches only the numeric
response, finding the boundaries of the desired interval:
int ans = a[0],
sum = 0;
for (int r =0; r <n; + +
R) { sum + =
a[r];
ans = max (ans,
sum); sum = max
(sum, 0);
}
Full version of the solution, maintaining the boundaries of the desired index-segment:
if (sum < 0) {
sum = 0;
minus_pos = r;
}
}
Related tasks
for (T. Chan 2007 "More algorithms for all-pairs shortest paths in weighted graphs").
This algorithm Chan, and others in the art results in fact describe rapid multiplication matrices (Where the multiplication
of matrices implied modified multiplication instead of addition uses a minimum, and instead of multiplying - Addition). The
fact that the problem of
finding submatrices with the largest sum is reduced to the problem of finding the shortest paths between
all pairs of vertices, and the task in turn - is reduced to the matrix multiplication.
This problem lies in the fact that it is necessary to find an interval The average of the maximum it was:
Of course, if desired by the segment condition not imposed other conditions, the solution will always be
be a segment of length at the maximum-array. Problem makes sense only if there are additional
restrictions (Eg, the length of the desired interval is bounded below).
In such a case applicable the standard method when dealing with the problems of the average value: will
select the desired maximum average value binary search.
To do this, we need to learn how to solve this subproblem: given by And you should check whether the array subsegment
(Of course, additional satisfying all constraints of the problem), in which the mean value is greater than .
To solve this subproblem, subtract of each array element. Then our subproblem
actually turns into this: whether or not in the array subsegment positive amount. And this problem we
can solve.
So we got the solution for asymptotic behavior Wherein - Required accuracy -
time solutions for an array of length subtasks (Which may vary depending on the
specific additional constraints imposed).
requires a subsegment of the segment length at least with the maximum possible arithmetic mean.
An algorithm for solving this problem is quite complicated. Author of this algorithm - KADR
(Yaroslav Tverdohleb) - the algorithm described in his message on the forum.
Literature
+C+
Visual C + +, MFC
● Kruglinski, Uingou, Shepherd. Programming Microsoft Visual C + + 6.0
for professionals (PDF [in RAR], 54.4 MB)
+C+
● ANSI. C + + International Standard (second edition, 2003-10-15) (PDF, 2.3 MB)
● Eckel. Philosophy C + +. Introduction Standard C + + (2nd ed.) (DJVU, 6.4 MB)
● Eckel, Allison. Philosophy C + +. Practical Programming (DJVU, 6.5 MB)
● Sutter. Complex tasks on C + +. 87 puzzling examples decisions (DJVU, 3.8 MB)
● Sutter. New challenges for C + +. 40 new puzzling examples with solutions (DJVU, 3.6
MB)
● Stroustrup. Programming language C + + (2nd, enlarged ed.) (PDF, 2.9 MB)
● Stroustrup. The C + + Programming Language (3rd edition) (PDF, 3.4 MB)
● Abrahams, Gurtovoy. C + + Template Metaprogramming: Concepts, Tools, and Techniques from
Boost and Beyond (CHM, 0.62 MB)
● Dzhosyutis. C + +. Standard Library. For professionals (DJVU, 4.8 MB)
● Dzhosyutis. C + +. Standard Library. For professionals (CD the book) (ZIP, 0.14 MB)
● Vandervoorde, Josuttis. C + + Templates: The Complete Guide (CHM, 0.72 MB)
● Sutter, Alexandrescu. C + + Coding Standards: 101 Rules, Guidelines, and Best Practices
(CHM, 0.51 MB)
● Golub. Rope sufficient length to shoot yourself in the foot. Terms of
programming in C and C + + (PDF, 1.29 MB)
● Meyers. Effective C + +. More effective C + + (CHM, 2.0 MB)
● Dyuherst. Slippery C + +. How to avoid problems when designing and compile your
programs (DJVU, 9.3 MB)
● Dyuherst. C + +. The sacred knowledge (DJVU, 6.7 MB)
Algorithms
Fundamental benefits
● Corman, Leiserson, Rivest, Stein. Algorithms. Design and analysis (2nd bldg.) (DJVU, 18.3 MB)
● Knuth. The Art of Computer Programming. Volume 1 (3rd edition) (DJVU, 6.0 MB)
● Knuth. The Art of Computer Programming. Volume 2 (3rd edition) (DJVU, 7.6 MB)
● Knuth. The Art of Computer Programming. Volume 3 (2nd edition) (DJVU, 7.7 MB)
● Corman, Leiserson, Rivest, Stein. Algorithms. Design and analysis (1st ed.?) (PDF, 4.5 MB)
● Sedgwick. Fundamental Algorithms (3rd ed.). Parts 1-4 (DJVU, 15.0 MB)
● Sedgwick. Fundamental Algorithms (3rd ed.). Part 5 (DJVU, 16.7 MB)
● Knut. Art of Computer Programming. Tom 1 (DJVU, 5.6 MB)
● Knut. Art of Computer Programming. Tom 2 (DJVU, 6.1 MB)
● Knut. Art of Computer Programming. Tom 3 (DJVU, 6.4 MB)
● Graham, Knuth, Patashnik. Concrete Mathematics (DJVU, 8.9 MB)
● Papadimitriou, Stayglits. Combinatorial Optimization: Algorithms and Complexity (DJVU, 5.6 MB)
● Motwani, Raghavan. Randomized Algorithms (DJVU, 4.4 MB)
● Tucker. Computer Science Handbook (PDF, 27.0 MB)
● Mehlhorn, Sanders. Algorithms and Data Structures: The Basic Toolbox (PDF, 2.0 MB)
Olympiad problems
● Menshikov. Olympiad programming tasks (DJVU, 4.4 MB)
● Menshikov. Olympiad programming tasks (CD the book) (ZIP, 4.0 MB)
● Okulov. Programming algorithms (DJVU, 3.6 MB)
● Dolinsky. Addressing the complex and Programming Olympiad problems (DJVU, 2.9 MB)
● Skiens, Reville. Olympiad programming tasks (DJVU, 5.3 MB)
Line
● Gasfild. Lines trees and sequence algorithms (DJVU, 12.1 MB)
● Smyth. Computing patterns in strings (DJVU, 26.4 MB)
● Crochemore, Rytter. Jewels of Stringology (DJVU, 2.6 MB)
● Crochemore, Hancart. Automata for matching patterns (Pdf, 0.44 MB)
Compilation, interpretation
● Aho, Lam, Sethi, Ullman. Compilers: Principles, Techniques and Tools (DJVU, 5.7 MB)
● Mogensen. Basics of Compiler Design (PDF, 0.81 MB)
● Pratt Zelkovits. Programming Languages: development and implementation (4th ed. 2002)
(DJVU, 5.7 MB)
Game theory
● Conway. On Numbers and Games (DJVU, 2.1 MB)
Computational Geometry
● Drug Shamos. Computational Geometry. Introduction (DJVU, 4.5 MB)
● Andreeva, Egorov. Computational geometry on the plane (DPF, 0.61 MB)
● Mount. Lecture notes for the course Computational Geometry (PDF, 0.77 MB)
● de Berg, van Kreveld, Overmars, Schwarzkopf. Computational Geometry: Algorithms
and Applications (2nd, revised edition) (DJVU, 3.7 MB)
● Chen. Computational Geometry: Methods and Applications (PDF, 1.14 MB)
● Starlings. Delaunay triangulation and its application (PDF, 2.5 MB)
● Miu. Voronoi Diagrams: lecture slides (PDF, 0.14 MB)
● Held. Voronoi Diagram: slides (PDF, 1.35 MB)
Counts
● Ahuja, Magnanti, Orlin. Network flows (DJVU, 13.8 MB)
● Priezzhev. Dimer problem and the Kirchhoff theorem (PDF, 1.18 MB)
● Thorup. Unidirected Single-Source Shortest Paths with Positive Integer Weights in Linear
Time (PPT, 1.10 MB)
● Eppstein. Finding the K Shortest Paths (PDF, 0.18 MB)
● Sokkalingham, Ahuja, Orlin. Inverse Spanning Tree Problems: Formulations and Algorithms
(PDF, 0.07 MB)
● Ahuja, Orlin. A Faster Algorithm for the Inverse Spanning Tree Problem (PDF, 0.10 MB)
● Brander, Sinclair. A Comparative Study of K-Shortest Path Algorithms (PDF, 0.16 MB)
● Gabow. An Efficient Implementation of Edmonds Maximum-Matching Algorithm (PDF, 2.7 MB)
● Bender, Farach-Colton. The LCA Problem Revisited (PDF, 0.08 MB)
● Maynika. Optimization algorithms for networks and graphs (DJVU, 4.0 MB)
● Mehlhorn, Uhrig. The minimum cut algorithm of Stoer and Wagner (PDF, 0.12 MB)
● Åre. Graph Theory (DJVU, 4.3 MB)
● Harari. Graph Theory (DJVU, 8.7 MB)
● Stoer, Wagner. A Simple Min-Cut Algorithm (PDF, 0.20 MB)
● Lovasz, Plummer. Matching theory (PDF, 9.9 MB)
● Tutte. The Factorization of Linear Graphs (PDF, 0.47 MB)
Combinatorics
● Stepanov. Burnside lemma and the problem of coloring (DPF, 0.18 MB)
● Harari. Enumerating graphs (DJVU, 4.1 MB)
Complexity Theory
● Gary Johnson. Computers and intractability of the problem (DJVU, 11.5 MB)
Mathematics
● Aitken. Determinants and Matrices (PDF, 10.2 MB)
Data Structures
● Tarjan. Efficiency of a Good But Not Linear Set Union Algorithm (PDF, 0.63 MB)
● Tarjan, Leeuwen. Worst-Case Analysis of Set Union Algorithms (PDF, 1.55 MB)
Optimization
● Kaspersky. Code Optimization: Effective Memory Usage (CHM, 10.4 MB)
● Kaspersky. Code Optimization: Effective Memory Usage (CD the book) (ZIP, 4.6 MB)
● Fog. Optimization Manuals (Optimizing software in C + +, in assembly,
processors microarchitecture) (last edited - 2008) (PDF [in ZIP], 2.9 MB)
● Intel. Intel Architecture Optimization Manual (1997) (PDF, 0.49 MB)
Java
Java
● Eckel. Philosophy Java (4th ed.) (DJVU, 5.4 MB)
● Horstmann, Cornell. Java 2. Library professional. Tom 1 (The Basics) (7th ed.)
(DJVU, 10.5 MB)
● Horstmann, Cornell. Java 2. Library professional. Tom 2 (Subtleties
programming) (7th ed.) (DJVU, 13.2 MB)
● Horstmann, Cornell. Java 2. Library professionals (7th ed.) (CD the book) (ZIP, 0.66 MB)
TeX
TeX
● Knut. All about TeX (DJVU, 17.1 MB)
● Abrahams, Hargreaves, Berry. TeX for the Impatient (PDF, 1.36 MB)
LaTeX
● Gratzer. Math into LaTeX. An Introduction to LaTeX and AMS-LaTeX (DJVU, 0.34 MB)
● Oetiker. The Not So Short Introduction to LaTeX (version 4.26, 2008-Sep-25) (PDF, 2.3 MB)
About the Author
Permanent site author e-maxx.ru and all articles on algorithms I am, e-maxx, also known as
Maxim Ivanov :)
All materials, including this book, laid out under license Public DomainIe the public domain, not copyrighted
and can be distributed
is unlimited. The main objective of this resource - the dissemination of information and the elimination of
obstacles to this.
Except for me in creating, updating and improving articles attended many people - thank you for the
countless comments, suggestions for improvement, pointing out inaccuracies, etc. Unfortunately, none of
the community did not dare to write a full article describing any algorithm. If you wish to be the first -
welcome ;)
You can contact me by email: [email protected] by Forum Site e-maxx.