The Hitchhiker's Guide To The Programming Contests
The Hitchhiker's Guide To The Programming Contests
Do Panic
Nite Nimajneb
Contents
1 Dynamic Programming 1
1.1 Subset Sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 The Longest Increasing Subsequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Longest Common Subsequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3.1 Reduce the Space to One Dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Max Sum on a Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 The Best Triangulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Counting or Optimizing Good Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Graphs 5
2.1 Breadth First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Depth First Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2.1 Topological Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.2 Strongly Connected Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2.3 Cut Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Shortest Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.1 Bellman-Ford . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.2 Floyd-Warshall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.3 Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.4 The Shortest Path DAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4 Counting the Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5 The Minimum Average Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6 The Minimum Spanning Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7 Bipartite Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.8 Maximum Flows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.9 Minimum (Maximum) Cost Bipartite Matching . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.10 Minimum Cost (Maximum Profit) Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3 Numbers 22
3.1 the Greatest Common Divisor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2 Generating the Prime Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3 Repeated Squaring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.4 Long Integers using Character Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.5 Fractional Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4 Algebra 31
4.1 the Matrix Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2 the Method of Relaxation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5 Geometry 35
5.1 the Geometry Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.2 Some Geometric Facts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6 Miscellaneous 47
6.1 Binary Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.2 Range Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
6.3 Set Union and Find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.4 String Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.5 2D Arrays, Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
6.5.1 Cover the Chessboard with Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
i
6.5.2 2D Max Sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.5.3 Max Unaffected Rectangle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
ii
1 Dynamic Programming
1.1 Subset Sum
Problem 1.1 Given a set of n numbers ai sum up to M , and any K ≤ M , whether there is a subset of the
numbers such that they sum up to (hit) K? We assume n might be as big as 1000, but M or K is not too
big.
From the Guide: We use a one dimensional table m[0..M], m[b] indicate whether b can be hit.
int m[M+10];
Remark. The original idea is to use a 2 dimensional array, where each column only depends on the previous
column. By a programming trick we just need one column. But we need to write the j-loop in the reversed
way to avoid messing things up.
There are a lot of variants of the subset sum problem.
• Candy for two kids: The a[i]’s are thought as value of candies. We want to divide the candies as
evenly as possible between the two kids. Now the problem is not to hit a fixed K. We want to search
a K such that it is as close to M/2 as possible. We may simple compute the m array, and look up
which is the nearest ”yes” below M/2.
• Subset sum with multiple supplies: Each ai can be used as many times as you like in the sum,
are we going to hit K? Maybe you will go a long way or a short way to see the solution. But finally
the solution is just by reversing the direction of the j-loop in the subset sum program: for(j=a[i];
j<=K; j++).
• Coin change: Now think ai ’s are coins, you want to make an exact change of K. Maybe there are
multiple ways you can do this, then you want to minimize (or maximize) the number of coins you use.
The structure of the solution is not changed, we just need to change the meaning of the m array. Now
m[b] is no longer 0 or 1, it is exactly the minimum number of coins we need to hit b.
• Candy for three kids: We want to divide the candies as evenly as possible among three kids. It is
a question that what do we mean by “as evenly as possible”. The answer is that one can define it in
many different ways, but the structure of the solutions are almost the same: Use a two dimensional
array m[][], m[b][c] indicates whether or not we can divide the candies so that b of them goes to the
first kid, c of them goes to the second (so the rest goes to the third).
1
From the Guide: 1 m[i] to be the length of the longest increasing subsequence q of s[i..n-1] such that
q contains m[i] as its first element. To solve m[i], guess what will be the next element in the best sequence.
From the Guide: 2 m[i] to be the length of the longest increasing subsequence q of s[0..i] such that q
contains m[i] as its last element.
Both ways are good. But if they ask you to output the actual longest sequence, especially if they want
the lexicographical smallest one among all the solutions, the first key is better.
There are three other versions: descending, non-descending, non-increasing. You just need to change a
bit in the program.
The following improvement is really cute. It is a nice exercise to prove or to believe why it is correct.
set<int> st;
set<int>::iterator it;
...
st.clear();
for(i=0; i<n; i++)
{
st.insert(a[i]); it=st.find(a[i]);
it++; if(it!=st.end()) st.erase(it);
}
cout<<st.size()<<endl;
From the Guide: m[i][j] to be the length of the longest common subsequence of s1[i..M-1] and
s2[j..N-1]. To solve m[i][j], focus on the first step, if s1[i]==s2[j], then we will pick them in our com-
mon sequence (why picking them is no worse than not picking, this requires a 10 seconds proof); otherwise,
we must throw away at least one of them.
2
if(s1[i]==s2[j]) m[i][j] = 1+m[i+1][j+1];
else m[i][j] = max(m[i][j+1], m[i+1][j]);
}
cout<<m[0][0];
Remark. When all the symbols in s1 are distinct, the LCS problem can be reduced to the LIC problem. By
renaming the elements of s2 , according to the order they appear in s1 , the LCS problem is the same as finding
the LIS in s2 . So, when all elements in s1 are distinct, the problem can be solved in O((m + n) log(m + n))
time.
It is trivial to do the problem in O(n3 ) time. An O(n2 ) algorithm is also very easy: Preprocess the
information such that s[i] records the sum from the beginning to the i-th position. The best solution,
hinted as the following, is O(n).
From the Guide: Let m[i] be the maximum sum of any subarray that ends at the element a[i]. Then
m[i] is simply max(a[i], m[i-1]+a[i]).
3
Our question here is, among so many triangulations, which is the best one? The word “best” can be
defined in many ways. Let say best means the sum of the lengths of the diagonals we pick is as small as
possible.
From the Guide: Look at the edge v0 vn−1 , in any triangulation it is contained in exactly one triangle.
Guess what will be the other point of that triangle. Let m[a][b] (a < b) be the value of the best triangulation
of the (part of) polygon va va+1 · · · vb va .
This time, the solution of the longer segments depends on the shorter segments.
m[..][..] = 0;
for(len=3; len<n; len++)
for(a=0; a+len<n; a++)
{
b=a+len; m[a][b]=1e+10;
for(c=a+1; c<b; c++)
{
double t=m[a][c]+m[c][b];
if(c>a+1) t+=length(a to c);
if(c<b-1) t+=length(c to b);
m[a][b] <?= t;
}
}
Problem 1.6 Given a directed acyclic graph, how many paths are there from u to v? What is the longest
one if there are weights on the edges?
From the Guide: Do a topological sort (Section 2.2.1) on the graph, then do a DP along the topological
order.
Remark. You do not need to do the DFS (topological sort) and DP separately. Just do the DP in recursive
manner. The situation where the recursive DP is good is exactly when the order of the DP is hidden.
Remark. The longest increasing subsequence problem is a special case of the longest path in a DAG.
The longest path in an undirected graph is not well defined, if we allow repeated vertex on a path; and
it is well defined but extremely hard. A naive algorithm checks all possible (at most will be n!) paths. We
are going to improve, roughly, n! to 2n .
Problem 1.7 (the Traveling Salesman Problem) In a graph G, a salesman start from the vertex 0,
visit all the other vertices at least once and come back to the starting vertex. What is the fastest of such a
tour?
From the Guide: play(a, S) (a ∈ S) solves the question of the fastest route start from a, visit each point
in S at least once, and go back to 0. To solve it, we focus on what will be the next step. If that is i for some
i ∈ S, then we have d(a, i) + play(i, S \ {a}), where d(a, i) is the shortest path from a to i.
4
The complete code is in Problem 7.4.
Another famous problem that is very similar is called the Hamilton cycle problem, where we require each
vertex be visited exactly once. The question is whether there exist Hamilton cycles. If yes, what is the
shortest one? On general graphs, this question is slightly simpler than the TSP problem — we just need to
change the d(a, i) to r(a, i), i.e., we use the length of an edge instead of the shortest path.
Problem 1.8 In a 0-1 matrix, how many ways we can pick n 1’s such that each row and each column
contains exactly one picked element?
Problem 1.9 On a n × n chess board we want to put n rooks such that no two rooks can attack each other.
In how many ways we can do this? Well, n!. But what if we forbid some squares on the board?
From the Guide: play(a, S) (|S| = a), where S is a set of columns, solves count how ways we can put
rooks in the first a rows such that the set of column occupied is exactly S.
What if we want to put k rooks instead of n rooks? Go through the problem again, the key is almost
the same, but we do not require |S| = a.
2 Graphs
Usually n is the number of vertices, and m (although most likely only for discussion purposes) is the number
of edges. We usually use r[][] to represent the (adjacency matrix of the) graph, it is a 0-1 matrix if the
graph is not weighted and simple, it is symmetric if the graph is undirected, etc. In some algorithms, like
Floyd, we start with r[][] as the adjacency matrix, then it evolves to the matrix represent the all-pairs
distances.
struct ND
{
... state // information of the state
int d; // the depth. i.e. the distance from the start state
int f; // parent pointer
};
The array mac[] and pointers born and dead realizes the queue for BFS. The queue is always the nodes
from mac[dead] to mac[born-1], inclusive. The predicate visited can be realized by a 0-1 indicator array,
a map, or a set. In the first two cases we may combine ND.d and visited together.
Outline: BFS
ND mac[??];
int born, dead;
int visited[??];
5
int BFS()
{
ND nd, nnd;
Initialize visited to be no; born = dead = 0;
mac[born] = start; born++; // there might be several start points
visited[start] = yes;
while(dead < born)
{
nd = mac[dead];
if(nd.state == goal) {output; return 1;}
for each transition nd.state --> v
if(!visited[v])
{
nnd.state = v; nnd.f = dead; nnd.d = nd.d+1;
mac[born] = v; born++; visited[v] = yes;
}
dead++;
}
output "no solution"; return 0;
}
dfs(a) will mark (visit) all the vertices that are reachable from a. In case of undirected graphs, this
means the component containing a. To find the number of components in a undirected graph, as well as
mark the components with a representative using v, we do
...
int cpt=0;
for(i=0; i<n; i++) v[i]=-1;
6
for(i=0; i<n; i++) if(v[i]<0)
{
cpt++; rep=i;
dfs(i);
}
...
The so called DFS tree (or DFS forest) is the diagram based on the recursive calls of the DFS. A tree
edge a → b means dfs(a) is the first one to call dfs(b). The children of a node is positioned from left to
the right, according to the order they are called. All the other edges are dotted around the tree, they are
discovered but never actually traveled. a → b can be classified as back edge if b is an ancestor of a in the
tree; forward edge if b is a descendant of a; and cross edge otherwise. Many algorithms are depend on some
important observations about the DFS tree. For example: If the graph is undirected, there will never be a
cross edge in the DFS tree. If the graph is directed, there will never be cross edges from left to right.
void dfs(int a) {
if(v[a]==1) fg = 1;
if(v[a]) return;
v[a] = 1; // gray
for each neighbor a->i dfs(i);
v[a] = 2; // black
od[p] = a; p--;
}
...
for(i=0; i<n; i++) v[i]=0; //white
fg=0; p=n-1;
for(i=0; i<n; i++) if(!v[i])
dfs(i);
if(fg) say there is a cycle;
...
7
component that can reach a lot of other components. The SCC algorithm is basically two DFS, the first
DFS do a rough topological sort to ensure the second DFS will be lucky. In the second DFS, instead of
for(i=0; i<n; i++) if(!v[i]) dfs(i);,
we go according to the order given by the first DFS. There are more details. The complete program is
void dfs(int a) {
if (v[a]) return;
v[a] = 1;
for (int i = 0; i < N; i++)
if(r[a][i]) dfs(i);
top_place--;
order[top_place] = a;
}
void top() {
int i;
top_place = N;
for(i=0; i<N; i++) v[i]=0;
for(i=0; i<N; i++) if(!v[i]) dfs(i);
}
void scc() {
int i;
top();
for(i=0; i<N; i++) v[i]=0;
for(i=0; i<N; i++)
{
int j = order[i];
if (!v[j]) {
curr_comp = j;
dfs_rev(j);
}
}
}
8
2.2.3 Cut Point
Based on the observation that there is no cross edges in the DFS tree. So, assume v is a node at level a, and
T is a subtree of v, after deleting v, the only chance to save T ’s connection to the rest of the world is a back
edge from T to somewhere higher than a. We may detect the cut-points as well as how many components
are created if we cut a certain point. The complete code is in Problem 7.6.
You need to pay attention to how the cut-points are defined if the input graph is already disconnected.
Outline: Bellman-Ford
9
Even if there is a negative cycle from 0, for a particular v, it is still possible that the shortest path from
0 to v is well defined. d[v] is not well defined (or −∞) if and only if there is a negative cycle C such that
0 → C and C → v. To detect this a simple Bellman-Ford is not enough. This can be done very neatly, see
the next section.
2.3.2 Floyd-Warshall
Solves the all-pairs shortest path problem in O(n3 ) time.
there is a simple negative cycle C such that 0 can reach C and C can reach v. (1)
Bellman-Ford can only detect if there is a negative cycle reached by 0, but that cycle not necessary reach v.
Even we iterate many more times, we are not promised to decide if (1) happens.
There are several solutions to (1). We can either use a Bellman-Ford combined with a reversed Bellman-
Ford from v, or Bellman-Ford combined with a reversed DFS. But the best solution is by Floyd. Since Floyd
has the nice properties:
• After n loops, r[i][j]<infinity iff there is a path from i to j.
• If i is involved in a simple negative cycle, then after n loops r[i][i]<0. (The converse is not necessarily
true.)
Based on these properties, we can solve complete the shortest path problem with possible negative cycles.
2.3.3 Dijkstra
One important restriction of this algorithm is that it only works for graphs with non-negative weights. The
running time is O(n2 ). With a little effort it can be made into O(m log n)
It starts from the source vertex, in each stage there will be one new vertex marked. All the marked
vertices consists of the civilized world, and the rest of the vertices consists on the unknown world. In each
10
stage, we examine all the edges go from the civilized world to the unknown, to update the estimations of the
distance of vertices in the unknown world. The one with the smallest estimation is the next vertex to mark.
We assume the source is s. We use d[] to store the estimation of distances, at the end they will be the
actual distances. We use mk[] to see if a vertex is marked or not.
void init()
{
int i;
n = ?;
for(i=0;i<n;i++)
{
r[i].clear();
e[i].clear();
}
// read the graph???
for(i=0;i<n;i++) dist[i]=-1;
}
11
while(!h.empty())
{
it=h.begin();
v=(*it).second; // the node
d=(*it).first; // the distance
h.erase(it);
for(i=0;i<r[v].size();i++)
// for each neightbor of v
{
tmp=d+e[v][i];
j=r[v][i];
if(dist[j]<0 || tmp<dist[j])
{
dist[j]=tmp;
pa[j]=v;
h.insert(multimap<weight, int>::value_type(tmp, j));
}
}
}
}
i.e., one of the shortest paths from 0 to v goes through u. In this case we call u a parent of v. In all of our
shortest path algorithms, after we find the actual distances, it is easy to find one or all of v’s parents by (2).
(Well, v might have more than 2 parents...) Many problems are based on Topological sort of a DAG plus
dynamic programming. For example, there might be many shortest paths from 0 to v, if we want to count
how many of them, we can do a dynamic programming on the shortest path DAG.
Everything can be reversed. Bellman-Ford and Dijkstra solves the single source shortest path problem.
By modify a little bit they can solve the single destination shortest path problem. For example, if we want
to find the lexicographical smallest among all the shortest paths from 0 to v, we just need to define dd[u]
to be the distance from u to v, and
int st=0;
while(st != v)
{
cout<<st<<" ";
for(i=0; i<n; i++) if(dd[st]=dd[i]+cost(st, i))
{ st=i; break; }
}
cout<<v<<endl;
12
2.4 Counting the Paths
Problem 2.1 Let G be a graph (directed or undirected, weighted or un-weighted). How many paths are there
from u to v that takes exactly t steps?
From the Guide: Write down the adjacency matrix A, where Aij is the number of edges from i to j. Let
At be the usual matrix multiplication of A t times. The (u, v) entry in Atuv is exactly the answer we are
after. (Exercise: Check for t = 0, 1, 2.) If t is big, we can do the standard repeated squaring (Section 3.3).
Let us look at the same thing from a dynamic programming view point: A[t][i][j] be the number of
paths from i to j with exactly t steps. To compute A[t][i][j], we focus on which will be the first step:
n−1
A[t][i][j] = A[1][i][k] · A[t=1][k][j]
k=0
Problem 2.2 Let G be a graph (directed or undirected, weighted or un-weighted). Among all the paths from
i to j that takes exactly k steps, which one is of the shortest length?
Now we just need to modify the meaning of A[t][i][j], and the recurrence becomes
n−1
A[t][i][j] = min(A[1][i][k] + A[t-1][k][j])
k=0
If you like to think in the algebraic way, we just redefined the rule of matrix multiplication. Be aware
that if you use something like 1000000000 as infinity, and there are negative edges, then you need to some
tolerance about what should be considered real infinity. (For example, if all the edges are less than 1000,
and k < 2000, then anything as large as 1000000000 − 2000000 can be considered as infinity.)
In the outline, we also construct the actual path. p[t][i][j] records what is the first step on the best
path from i to j in t steps.
int BG=1000000000;
// to avoid overflow in addition, do not use 2^31-1
13
cout<<a<<" ";
a = p[t][a][b];
t--;
}
cout<<b<<endl;
}
The solution is an application of the previous section. A[t][x][x] gives the best cycle of length t that
involves x. The minimum average cycle is the one with minimum A[t][x][x]/t. To output the best cycle
is just to output the path from x to x in t steps, as use outlined in the previous section.
The running time of the above algorithm is O(n4 ). The feeling is that we are doing all pairs shortest
path. If we can reduced it to single source (or single destination) problem, maybe the running time can be
reduced by a factor of n. This is the right intuition:
We add a point n to the graph, and there is an edge to n from each vertex with weight 0. (But no edge
comes out of n.) Now we want to compute A[t][i] to be the shortest path from i to n with exactly t steps,
and p[t][i] to be the first step on such a path.
int BG = 1000000000;
To output the actual cycle we follow the p links, but this time we do not know the actual length.
14
{
int t=n+1;
int wk=st;
do {
cout<<wk<<" ";
wk=p[t][wk]; t--;
}while(wk!=st);
cout<<st<<endl;
}
Theorem 2.1 (the Matrix-Tree Theorem) Suppose G is a graph without self-loop (there might be mul-
tiple edges between two vertices, but no edge come from a vertex to itself.). Let M be the matrix where on
the diagonal we have the degrees, i.e., Mii is the degree of vi in G; and off diagonal we have, for i = j, the
negative number of edges, i.e., Mij = Mji is the negation of the number of edges between vi and vj . Let M
be a matrix by deleting any row and any column from M . Then the number of spanning trees of G equals
the absolute value of the determinant of M .
The theorem gives an algorithm to compute the number of spanning trees. As a special case, we have
Cayley’s formula.
Theorem 2.2 (Cayley’s Theorem) The number of spanning trees of a complete graph Kn is nn−2 .
Now, there are many spanning trees. (For n ≈ 16, you need a long long type to hold the answer.) If
the graph is weighted, we want to find the one with the minimum weight. There are two natural greedy
algorithm works.
The Prim algorithm resembles Dijkstra. It starts from a particular vertex v. We always focus on the
component we have for v, in each stage there will be one new vertex connected to the component. All the
marked vertices consists of the civilized world, and the rest of the vertices consists on the unknown world.
In each stage, we examine all the edges go from the civilized world to the unknown. The smallest such edge
will be chosen, and the other end of this edge will be marked.
int mst(int a)
{
int i,j,k,mini,tj,tk;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
r[i][j]=-1;
15
// Construct graph **symmetric [i][j]->[j][i]
int col[?]; // color 0 means not in the tree
for(i=0;i<n;i++) {col[i]=0; d[i]=maxWeight; pa[i]=-1;}
d[a]=0;
//col[a]=1;
//pa[a]=-1;
for(i=0;i<n;i++)
{
mini=maxWeight;
for(j=0;j<n;j++)
if(col[j]==0 && mini>d[j])
{ mini=d[j]; tj=j; }
if(mini==maxWeight) return(0);
col[tj]=1;
for(j=0;j<n;j++) if(col[j]==0)
if(r[tj][j]<d[j])
{
d[j]=r[tj][j];
pa[j]=tj;
}
}
return(1);
}
In Kruskal’s algorithm, we start from the empty graph, viewed as n isolated components. We want to
throw the edges back to connect them. The key is that we throw the edges back one by one, ordered by
their weight. We use an edge if it connects two different components, otherwise we do not need it.
eg e[250];
int u[250];
int f[200];
int findg(int a)
{
int st[200]; int pc=0;
while(a!=f[a])
{
st[pc]=a;pc++;
a=f[a];
}
while(pc) {pc--; f[st[pc]]=a;}
return(a);
}
int mst()
16
{
int i,x,y;
for(i=0;i<n;i++) f[i]=i;
int egs=0;
for(i=0;i<m;i++)
{
x=findg(e[i].x);
y=findg(e[i].y);
if(x!=y)
{
u[i]=1;f[x]=y; egs++;
//if time exceed, use a rank function
}
else u[i]=0;
}
if(egs==n-1) return(1);
return(0);
}
In the program we need a nice way to bookkeeping the components. The issue is called find-union, which
is the topic of Section 6.3.
Theorem 2.3 A graph G is bipartite if and only if there is no cycle in G of odd length.
It is easy to detect if a graph G is bipartite: Run a DFS with a parameter indicating the depth, there is
an odd cycle iff you find some vertex who is discovered before with the depth of a different parity.
If G is bipartite, there might be more than one way to partition the vertices into red and blue (left and
right). In fact, the number of such partitions is exactly 2t , where t is the number of connected components
of G.
The next question is the maximium bipartite matching problem.
Problem 2.4 (Biaprtite Matching) Suppose we are given a bipartite graph G as well as its bipartition.
G = (R, B, E), where R is the set of red (left, boy) vertices, B is the set of blue (right, girl) vertices, and E
is the set of edges. A matching is a set of edges so that no vertex is paired more than once. The size of the
matching is the number of pairs. We are interested in finding the maximum sized matching. i.e., we want
to pair them up as many as we can.
For bipartite graphs, we still use a 2D array r[][]. But now it is not necessarily square. r[i][j] is not
the relation between vi and vj ; now it gives information between ri and bj (the i-th boy and the j-th girl).
The algorithm for finding the max bipartite matching starts from the empty matching, and grow it bigger
and bigger. The way we grow it is by finding an augmenting path. An augmenting path for a matching M
is a path of the form r0 b1 r1 b2 r2 · · · bk rk bk+1 , such that all the edges on the path are in E(G), all the edges
bi ri is in the matching M , and neither r0 nor bk + 1 is matched in M . If we have such a path, we can switch
the pairing to get one more pair in our matching. The nice theorem says that the converse is also true.
17
The program below finds the maximum matching, moreover, it gets the max matching with lexicograph-
ical smallest set for the left part if not all the left part can be satisfied.
In the variables below, we assume 110 is the upper bound for both left set and right set. The sizes may
vary.
int r[110][110];
int N,M;
int v[110];
int m[110], m1[110];
To use the code, you need to set up N to be the number of vertices in the left set, M to be the number
of vertices in the right set, and r[i][j] to be 1 if there is an edge from the i-th left vertex to the j-th right
vertex. Then, call bipMatch(), it will return the size of the max matching. And after the call the array
element m[i] indicates the partner of the i-th left vertex (−1 means it is not matched to anyone).
int dfs(int a)
{
if(a<0) return(1);
if(v[a]) return(0);
v[a]=1;
int i;
for(i=0;i<M;i++) if(r[a][i]) //* see remark
{
if(dfs(m1[i]))
{
m[a]=i;m1[i]=a;
return(1);
}
}
return(0);
}
int dfsExp(int a) {
int i;
for(i=0;i<N;i++) v[i]=0;
return dfs(a);
}
int bipMatch()
{
int i;
int ans=0;
for(i=0;i<N;i++) m[i]=-1;
for(i=0;i<M;i++) m1[i]=-1;
for(i=0;i<N;i++) if(m[i]<0) ans+=dfsExp(i);
return(ans);
}
Remark. The line with a * loops over all the possible partners of the a-th left vertex. We have some
freedom here. For example, if in some applications double r[][] represents the distances, and a match is
18
possible if the distance is no bigger than a threshold D, then the line becomes
In some applications we need linked list representation of the graph, since the graph is sparse and we
could not afford a matrix, then we do not use r[][], instead define something like vector<int> r1[1010].
And the loop becomes
Matching can be defined on general graphs: A set of edges such that no vertex is touched (paired) more
than once. In a general graph, the maximum matching is still computable in polynomial time, but it is much
more complicated than the bipartite case.
A set S of vertices is called a vertex cover for G if each edge of G contains at least one end point in S.
Consider the vertices and edges are the map of a city, you want to pick several intersections to put police
so that every road is guarded. The minimum vertex cover is the least number of polices you need to put in
order to guard all the roads. It is easy to see the minimum vertex cover number is at least the maximum
matching number, since no vertex can guard two edges in a matching. In general, these two numbers are not
equal. And the vertex cover number is likely to be very hard – the problem is NP-complete. Nevertheless,
we have a nice theorem in the bipartite case.
Theorem 2.5 In a bipartite graph G, the vertex cover number is the same as maximum matching number.
Problem 2.6 (Min Cut) Given a graph G, a source s and a destination t, and on each edge e, there is a
cost c(e) ≥ 0. We want to delete some edges so that t is disconnected from s. (i.e., there is no directed path
from s to t; but there might be path from t to s). What is the minimum cost of such a cut?
In a weighted graph G with weights c(e), we view c as capacity in the max flow problem, and view c as
the cost in the min cut problem. It is easy to see that any flow cannot transfer more than the cost of any
cut. So max flow is at most the min cut. The magic theorem says that they are actually equal.
Theorem 2.6 (max flow min cut) In any weighted graph, max flow = min cut.
As a convention, we focus on the problem of finding the max flow (the amount as well as the actually
flow). We will find the cut that achieves the min cut as a by-product of our max flow algorithms.
Before we describe the algorithms, we show some applications of max flow. First, we have the following
theorem that allows us to work on discrete units instead of continuous substance.
Theorem 2.7 If G is a graph where all the capacities are integers, then among all the possible max flows,
there is one where the amount of substance flowing on each edge is integer.
19
In fact all our algorithm will give such an integer flow if the input capacities are integers.
Application. If we have a bipartite graph G = (A, B, E), think of A is the vertices we draw on the left,
B on the right side. We add a leftmost point s and rightmost point t, and add edges between s and each
point in A, and t to each point in B. (We may direct each edge from left to the right, or just allow both
directions.) And we think each edge has capacity 1, then the max matching problem becomes a special case
of the max flow problem.
Application. If we have a graph G and two vertices s and t. We want to know at most how many edge-
disjoint paths (meaning no edge can be used on two paths, but a vertex may be used several times) from s to
t we can find. A nice theorem in graph theorem tells us that this is the same as the question of at least how
many edges we need to cut off so that s and t are disconnected. This is nothing but the min cut problem
where we think each edge has both direction with capacity 1. So we can solve this by a max flow.
Application. How about the restrictions (the capacities) are on the vertices instead of edges? Aha, here
is the beautiful construction: We draw the graph, and draw it again on a slightly higher level, say, second
floor. Now, each node v has two copies, one on each floor. We call the one on the first floor vin (the entrance
of v), and the one on the second floor vout (the exit). Now we redraw the edges, if there was an edge u → v,
we add an edge uout → vin , with capacity big enough, say, bigger than the max capacity on any vertex in
the original graph. At last, for each vertex v in the old graph, we add in the new graph an edge vin → vout
with capacity exactly the same as the capacity on v in the old graph. By doing this, we simulated the flow
problem with vertex capacities with a usual one where the capacities are on edges. The answer we want is
the max flow from sin to tout .
Application. Given a graph G and two vertices s and t, we want to know at most how many vertex-disjoint
paths from s to t we can find. This is just a special case of the previous application, where we think each
vertex except s and t has capacity 1. See Problem 7.7 for an example.
The basic algorithm for max flow is simple. We star from the empty flow, the residue capacity is
c(e) − f (e), meaning how many capacity left on an edge. (Note that if we flow along an edge u → v with
flow x, the capacity c(v, u) is also changed, it is increased by x. We may think a flow of x from u to v is
also a flow of −x from v to u. So, even originally the capacity of (v, u) is 0, it may become positive in our
algorithm.)
At any stage we have a flow f , the residue network is the graph consists of all the edges with positive
residue capacity. A path P from s to t in the residue network is call an augmenting path for f . Clearly,
we can transfer x more substance from s to t along the augmenting path, where x is the minimum residue
capacity on the path P . (Finding a path is easy, use DFS, BFS, or even Bellman-Ford.)
We do this again and again, until the residue network becomes disconnected. (t is not reachable from s.)
There is a theorem assures us that the flow we found is actually the max flow. And let A be the vertices
that are reached in the final residue network, B be the rest of the world, all the edges from A to B in the
original graph gives a min cut.
In the worst case, this algorithm is slow — it’s even not promised to run in polynomial time. But if we
always use BFS in finding the augmenting path, the algorithm can be proved in polynomial time.
We do not provide the code for this algorithm. But we do provide it under another name. A more general
algorithm solves something more: the min cost max flow (Section 2.10).
The algorithm we are going to use is more sophisticated. We sketch the outline below, the complete code
is identical to the solution to Problem 7.7.
Instead of sending some extra flow all the way to t each time, in this algorithm we send as much as possible
from s, those excessive substance that cannot reach t will flow back. Such a flow, where the capacities are
20
obeyed, but not necessary all the substance are reaching t, is called a preflow. The initial preflow is
c(e) if e goes out of s
f (e) =
0 otherwise
Given any preflow, the excessiveness of a vertex, ex(v), is define to be the amount of substance flow into
v minus the amount flow out of v. If all the substance reach t, (i.e., the preflow is actually a flow) then any
v other than s, t has excessiveness 0. We call a vertex active if it has positive excessiveness.
We will keep the label function φ so that φ(s) = n, φ(t) = 0, and φ(u) ≤ φ(v) + 1 for any edge u → v. We
initialize the labels φ(s) = n, and φ(v) = 0 for any other v. At any stage, an edge u → v is called admissible
if φ(u) = φ(v) + 1.
void relabel(v){
phi[v] := min(phi[w]+1), where w runs over all edges
v->w in the residue network
}
...
f(u, v) = 0 for all edges;
f(s, v) = c(s,v) for all v;
phi[v] = 0 for all v; phi[s] = n;
while (there is active vertex)
pick an active vertex v
if no edge v->w is admissible then relabel(v);
else pick an admissible edge v->w and push(v,w);
...
At the end, the max flow is the sum of all flow out of s. To find the min cut, we may do a simple DFS
on the residue network. (See the discussion in the Edmonds-Karp algorithm.)
We start with an arbitrary matching where the points left part are all matched, and repeat improving it,
until there is no improvement, and we can prove at that time the matching is the min cost one.
To do the improvement, we draw an ”improvement graph”. For any u and v, we put weight x on the
edge from u to v, if we give the current partner of v to u, we will gain cost x. i.e. x = c(v, m[v]) − c(u, m[v]).
Now it is clear a negative cycle in the improvement graph means if we cyclicly switch the partners along
that cycle, we will have a better matching.
The complete code is in the first solution to Problem 7.8.
21
2.10 Minimum Cost (Maximum Profit) Flow
Problem 2.8 (Min Cost (Max Profit) Flow) Given a graph G, each edge e has a capacity c(e), as well
as a unit cost (profit) Cost[e], and there are two vertices s and t. Among all the maximum flows from s to
t, which one costs the least (biggest)? The cost of an edge is computed by the unit cost times the number of
units we transfer along that edge, i.e. f(e)*Cost[e].
Remark. . We usually assume the costs are non-negative in finding the min cost flow. The real trouble
occurs when there is a negative cycle, in this case we can always find cheaper and cheaper (negative cost)
flows. The same trouble comes if we have positive cost cycles in the max cost flow problem.
Application. A slightly general question. If there are some set A of vertices with supplies S(v), v ∈ A, and
another set B with demands D(w), w ∈ B, and v∈A S(A) = w∈B D(w). We are interested in whether
we can transfer the substances so that all the supplies are out, all the demands are satisfied, and no other
points has any substance. If this is possible, there might be multiple ways to do it, what is cost for the least
cost (most profit) way?
To solve this question, we simple add two vertices s and t. Add edges from s to each one v ∈ A with
capacity S(v) and cost 0, and each one w ∈ B to t with capacity D(w) and cost 0. Then we solve the
min(max)
cost flow problem on the new graph. The original job is possible if and only if the amount of max
flow is v∈A S(A), and if it is possible, it gives the min(max) cost way to transfer the substance.
Application. (Min / Max cost matching in bipartite graph revisited) This is very similar to the previous
application. Add s and t. Edge s to the left part vertices with capacity 1 and cost 0, and the right part
vertices to t with capacity 1 and cost 0. The second solution to Problem 7.8 illustrates this.
The complete code is in the second solution to Problem 7.8. (The part before main(). In main() function
we illustrate what should be initialized.) The outline is surprisingly short:
Well, we need to say what do we mean by ”shortest path”, since we have costs and capacities. Clearly,
the natural guess will be the costs, and this is a correct guess.
We can use any shortest path algorithm to find P . Especially we may use Bellman-Ford, if there are
negative costs.
Remark. As you see, this algorithm solves something much more than the max flow problem with almost
no extra work than a max flow program.
3 Numbers
We list some basic Facts.
• If p is a prime and a ≡ 0 mod p, ap−1 ≡ 1 mod p; if a(p−1)/2 ≡ 1 mod p then there exist b such that
b2 ≡ a mod p.
22
• Let n be a positive integer greater than 1 and let its unique prime factorization be pe11 pe22 . . . pekk where
ei > 0 and pi is prime for all i. Then the Euler Φ function
1 1 1
Φ(n) = n(1 − )(1 − ) . . . (1 − )
p1 p2 pk
describes the number of positive integersco-prime to n in [1..n]. As a special case, Φ(p) = p − 1 for
prime p. The number of divisors of n is i (ei + 1).
• Euler’s Theorem, which extends Fermat’s Little Theorem: If (a, n) = 1, aΦ(n) ≡ 1 mod p.
int i,j;
for(i=0;i<1000000;i++) pr[i]=1;
pr[0]=pr[1]=0;
for(i=2;i<1000000;i++) if(pr[i])
for(j=i+i;j<=1000000;j+=i) pr[j]=0;
If you want to compress the prime numbers, i.e, not a 0-1 array of indicators, but actually a list of all
the primes less than 1000000, you may do
23
int pr[1000010], p[100010], cnt=0;
// pr is the indicator, p is the compressed table,
// usually 1/10 of pr is enough.
...
int i,j;
for(i=0;i<1000000;i++) pr[i]=1;
pr[0]=pr[1]=0;
for(i=2;i<1000000;i++) if(pr[i])
{
p[cnt]=i; cnt++;
for(j=i+i;j<=1000000;j+=i) pr[j]=0;
}
In the code above, pr[] and p[] can be the same array, if we will never need the 0-1 indicator later in
the program.
If we are only interested in whether a single number a is prime, we just need to test whether a is divisible
by b for any b2 ≤ a; or we just need to test if a is divisible by b for any prime number b where b2 ≤ a. Given
a pre-computed prime table, the complexity of this job for a = 109 is roughly 104 . Another faster way to
test the prime numbers will be presented in the next section.
Note that you need to be careful when do you need long long, when do you need unsigned long long,
and when even these are not enough.
Below is a fast program to test if a number up to 232 − 1 is prime. It is based on the Rabin-Miller Strong
Pseudoprime Test. In fact, if we replace the set {2, 7, 61} by the set of first 8 primes {2, 3, 5, 7, 11, 13, 17, 19},
we have a prime number test for all numbers up to 3 · 1014 . The time complexity is roughly 200 steps in the
worst case, and less than 100 steps on average.
24
int suspect(long long b, int t, long long u, long long n)
{
long long prod=1;
while(u)
{
if(u&1) prod=((prod*b)%n);
b=((b*b)%n);
u/=2;
}
if(prod == 1) return 1;
25
for(i=0;v[i];i++)
{
d=((v[i]-’0’)+c);
c=d/10;d%=10;
v[i]=’0’+d;
}
while(c)
{
v[i]=’0’+(c%10);
c/=10;i++;
}
v[i]=’\0’;
}
26
int l1=strlen(v1);
int l2=strlen(v2);
for(i=l1;i<l2;i++) v1[i]=’0’;
for(i=l2;i<l1;i++) v2[i]=’0’;
for(i=0;i<l1||i<l2;i++)
{
d=(v1[i]-’0’)+(v2[i]-’0’)+c;
c=d/10;d%=10;
v1[i]=’0’+d;
}
while(c)
{
v1[i]=’0’+(c%10);
c/=10;i++;
}
v1[i]=’\0’;
v2[l2]=’\0’;
}
27
}
28
{
int i;
if(strcmp(v2, "1")==0)
{
strcpy(v3, v1);
strcpy(v4, "0");
return;
}
if(strcmp(v1, "0")==0)
{
strcpy(v3, "0");
strcpy(v4, "0");
return;
}
for(i=0;v1[i];i++) v3[i]=’0’;
v3[i]=’\0’;
int ff=1;
int l=i;
for(i=l-1;i>=0;i--)
{
while(1)
{
if(v3[i]==’9’) break;
v3[i]++;
multi(v3, v2, v4);
if(comp(v4, v1)>0)
{
v3[i]--;
break;
}
ff=0;
}
if(ff && i) v3[i]=’\0’;
//simplify(v3);
}
multi(v2, v3, tmp1);
strcpy(v4, v1);
subs(v4, tmp1);
}
29
int i; char cc;
for(i=0;i<l-1-i;i++)
{
cc=v[i];v[i]=v[l-1-i];v[l-i-1]=cc;
}
}
struct fr {
int a,b; // long long? in that case also change gcd to long long
// fractional number a/b
};
30
adjFr(v);
}
4 Algebra
4.1 the Matrix Library
To invert a matrix M , we write down the identity matrix I to the right of M to make a n × 2n matrix, then
do a Gauss elimination to transform the left half to be I, the right half will be M −1 . The idea is that the
operations in the Gauss elimination are row reductions, the whole effect M is the multiplication of the row
reduction matrices. If M transform M to I, then M is M −1 , and it transform I to itself. The similar idea
is used to solve the equation M x = b (in any field).
Remark. Our matrix library is based on the real field. If you want to deal with the finite field Fp where p is
a prime number, you need to change several places: LD becomes int; after every addition and multiplication
you should mod p. After every substraction you should do x=(x%p+p)%p to avoid negative results. Every
division by r becomes the multiplication by r−1 in the field. Use Robert’s Theorem:
Theorem 4.1 (Robert Renaud) In the field Fp where p is a prime, if a = 0, the inverse of a is ap−2 mod
p.
Thus, we can use the repeated squaring (Section 3.3) to compute (or precompute) a−1 .
In the library we solve the equation Ax = b only when A is a invertible square matrix. If it is not
invertible, there might be no solution or infinitely many solutions, but we did not handle these cases in our
code.
Remark. The three lines in the solve() function is a nice place to see what kind of operations we can do
with our library.
LD EPS = 1e-8;
struct MATRIX
{
int n,m;
31
vector< vector<LD> > a;
32
{
vector<LD> rv(n, 0.0);
int i,j;
for(i=0;i<n;i++)
for(j=0;j<m;j++)
rv[i]+=a[i][j]*v[j];
return rv;
}
MATRIX operator*(MATRIX M1)
{
MATRIX R;
R.resize(n, M1.m);
int i,j,k;
for(i=0;i<n;i++)
for(j=0;j<M1.m;j++)
for(k=0;k<m;k++) R.a[i][j]+=a[i][k]*M1.a[k][j];
return R;
}
void show()
{
int i,j;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++) printf("%15.10f ", (double)a[i][j]);
printf("\n");
}
printf("end of the show \n");
}
};
LD det(MATRIX &M)
// compute the determinant of M
{
MATRIX M1=M;
LD r=M1.Gauss();
if(M1.n==0) return 0.0;
return r;
}
void show(vector<LD> v)
{
int i;
33
for(i=0;i<v.size();i++) printf("%15.10f ", (double)v[i]);
printf("\n");
}
• Electric Network: The graph is an electric network with resistors, the voltage on v0 is 0, and the
voltage on vn−1 is 1. What is the voltage on each vertex.
• The drunkard’s walk on a line: There are n stations on a line, the leftmost one, 0, is the home,
the rightmost one, n − 1, is the jail. The drunkard’s walk stops either he hits the jail or home. At any
other position, he move to the left or to the right with equal probability 1/2. Given any start position
i, let xi be the probability that he hits the jail first. Then the set of rules is: p0 = 0, pn−1 = 1, and
pi = 12 pi−1 + 12 pi+1 for all other i. (Focus on what happens on the first move.) Similarly, let Ei be the
expected number of steps he needs to take before he stops, how to solve Ei ? Now the rules becomes
E0 = En−1 = 0, and Ei = 12 (Ei−1 + 1) + 12 (Ei+1 + 1) = 1 + 12 (Ei−1 + Ei+1 ) for any other i.
• More general, the drunkard’s walk on a graph: Now he walks on a graph. v0 is still the home, and
vn−1 the jail. From each point, in one step, he will randomly choose one of the neighbors in the graph.
Formally, for 0 < i < n − 1, if vi has t neighbors u1 , · · · , ut , then the rule on vertex i is
1 1
pi = puj , and Ei = 1 + Euj
t j t j
.
• Even more general, the random walk on a graph: Now the person random choose a neighbor, but
not with equal probability. There is a fixed set of probabilities rij , where rij is the probability that, if
the person is at vi , he will choose vj for the next step. So, the reasonable condition is that for each i,
the probability going out of it sum up to 1. The rules are not any more complicated than the previous
problem.
Remark. I guess you guessed that the first problem and the last problem are actually the same: From any
vertex, the resistors adjacent to that vertex gives a set of probabilities (big resistors means harder to travel
via that edge). Let pi be the probability that we start from vi , do the random walk on the graph, we hit v0
before we hit vn−1 . pi is exactly the same as the voltage on vertex i. This is the beautiful relation between
random walks and electric networks.
Now comes the same idea occurred in Bellman-Ford and Floyd. This general idea is called relaxation:
We start from an arbitrary solution, and based on the rules we do iterations, after each round the solution
improves. And a nice thing here is that they improves fast, very quickly our solution converges to the real
solution.
Here is the simple solution to these problems:
34
//start from any initial values.
x[i]=0.0 for all i;
repeat enough times (*)
for each rule on i
x[i] = sum(r[i][j]*x[j])+c[i];
One word about the (*). You may check in each repetition what is the maximum amount of change in
xi ’s. Stop when the maximum change is negligible (say, less than 10−9 ). For most problems, the method
converges to the solution very quickly. You may simply say ”repeat 1000 times” or so.
5 Geometry
Here is our huge piece of planar geometry library. We list the whole code first, then make some remarks.
struct PT
{
double x,y;
int normalize()
// normalize the vector to unit length; return -1 if the vector is 0
{
double l = length();
if(fabs(l)<EPS) return -1;
x/=l; y/=l;
return 0;
}
PT operator-(PT a)
{
PT r;
r.x=x-a.x; r.y=y-a.y;
return r;
}
PT operator+(PT a)
{
PT r;
r.x=x+a.x; r.y=y+a.y;
return r;
}
PT operator*(double sc)
{
PT r;
r.x=x*sc; r.y=y*sc;
35
return r;
}
};
// =================================================================
// The Convex Hull
// =================================================================
36
//convex hull in n*n
void vex(vector<PT>& vin,vector<PT>& vout)
{
vout.clear();
int n=vin.size();
int st=0;
int i;
for(i=1;i<n;i++) if(vin[i]<vin[st]) st=i;
vector<int> used;
// used[i] is the index of the i-th point on the hull
used.push_back(st);
int idx=st; int next;
do{
next=0;
for(i=1;i<n;i++)
if(better(vin[i],vin[next],vin[idx]))next=i;
idx=next;
used.push_back(idx);
}while(idx!=st);
for(i=0;i+1<used.size();i++) vout.push_back(vin[used[i]]);
}
37
}
sort(vin.begin(), vin.end());
stk[0] = vin[0];
stk[1] = vin[1];
pstk = 2;
int isConvex(vector<PT>& v)
// test whether a simple polygon is convex
// return 0 if not convex, 1 if strictly convex,
// 2 if convex but there are points unnecesary
// this function does not work if the polycon is self intersecting
// in that case, compute the convex hull of v, and see if both have the same area
{
int i,j,k;
int c1=0; int c2=0; int c0=0;
int n=v.size();
for(i=0;i<n;i++)
{
j=(i+1)%n;
k=(j+1)%n;
int s=sideSign(v[i], v[j], v[k]);
if(s==0) c0++;
if(s>0) c1++;
if(s<0) c2++;
}
if(c1 && c2) return 0;
if(c0) return 2;
return 1;
}
38
// ===============================================================
// Areas
// ===============================================================
double trap(PT a, PT b)
{
return (0.5*(b.x - a.x)*(b.y + a.y));
}
double triarea(PT a, PT b, PT c)
{
return fabs(trap(a,b)+trap(b,c)+trap(c,a));
}
double height(PT a, PT b, PT c)
// height from a to the line bc
{
double s3 = dist(c, b);
double ar=triarea(a,b,c);
return(2.0*ar/s3);
}
// ====================================================
// Points and Lines
// ====================================================
39
double d = (p4.y - p3.y)*(p2.x-p1.x) - (p4.x - p3.x)*(p2.y - p1.y);
40
int bcenter( PT p1, PT p2, PT p3, PT& r )
{
// angle bisection
if( triarea( p1, p2, p3 ) < EPS ) return -1;
double s1, s2, s3;
s1 = dist( p2, p3 );
s2 = dist( p1, p3 );
s3 = dist( p1, p2 );
double rt = s2/(s2+s3);
PT a1,a2;
a1 = p2*rt+p3*(1.0-rt);
rt = s1/(s1+s3);
a2 = p1*rt+p3*(1.0-rt);
intersection( a1,p1, a2,p2, r );
return 0;
}
// ===============================================
// Angles
// ===============================================
41
r.y = sin(a)*p1.x+cos(a)*p1.y;
r = r+p0;
}
// ===============================================
// points, lines, and circles
// ===============================================
int lineAndCircle(PT& oo, double r, PT& p1, PT& p2, PT& r1, PT& r2)
// returns -1 if there is no intersection
// returns 1 if there is only one intersection
{
PT m;
closestpt(p1,p2,oo,m);
PT v = p2-p1;
v.normalize();
42
int CAndC(PT o1, double r1, PT o2, double r2, PT& q1, PT& q2)
// intersection of two circles
// -1 if no intersection or infinite intersection
// 1 if only one point
{
double r=dist(o1,o2);
if(r1<r2) { swap(o1,o2); swap(r1,r2); }
if(r<EPS) return(-1);
if(r>r1+r2+EPS) return(-1);
if(r<r1-r2-EPS) return(-1);
PT v = o2-o1; v.normalize();
q1 = o1+v*r1;
if(fabs(r-r1-r2)<EPS || fabs(r+r2-r1)<EPS)
{ q2=q1; return(1); }
double a=angle(r2, r, r1);
q2=q1;
rotate(o1, q1, a, q1);
rotate(o1, q2, -a, q2);
return 0;
}
43
if(y>0) j++;
}
return(j%2);
}
}
return 1;
}
void show(PT& p)
{
cout<<"("<<p.x<<", "<<p.y<<")"<<endl;
}
void show(vector<PT>& p)
{
int i,n=p.size();
for(i=0;i<n;i++) show(p[i]);
cout<<":)"<<endl;
}
void cutPoly(vector<PT>& pol, PT& p1, PT& p2, vector<PT>& pol1, vector<PT>& pol2)
// cut the convex polygon pol along line p1->p2;
// pol1 are the resulting polygon on the left side, pol2 on the right.
{
vector<PT> pp,pn;
pp.clear(); pn.clear();
int i, sg, n=pol.size();
PT q1,q2,r;
for(i=0;i<n;i++)
{
q1=pol[i]; q2=pol[(i+1)%n];
sg=sideSign(p1, p2, q1);
if(sg>=0) pp.push_back(q1);
if(sg<=0) pn.push_back(q1);
if(intersection(p1, p2, q1, q2,r)>=0)
{
if(pAndSeg(q1, q2, r)==1)
{
pp.push_back(r);
pn.push_back(r);
}
}
}
pol1.clear(); pol2.clear();
if(pp.size()>2) vex2(pp, pol1);
if(pn.size()>2) vex2(pn, pol2);
//show(pol1);
//show(pol2);
}
44
// ===================================================================
// UVA 137, the intersection of two CONVEX polygons.
// ===================================================================
// return 1 if the intersection is empty.
We think of the struct PT as both point and vector. Thus, PT(3,5) can be interpreted both as the
point (3, 5), or the vector from the origin to (3, 5). Especially, when we use the member functions of PT
(normalize(), +, -, *), we think it as a vector.
Extra care is always needed when we deal with precision problems and special cases in geometry. There
are many nice little tricks to deal with these, which are beyond the scope of this notes.
∞ (−1)n x2n x2 x4 x6
• cos x = n=0 (2n)! =1− 2! + 4! − 6! + ...
45
• e = 2.718281828459045235360287471352662497757
=limx→∞ (1 + x1 )x
= ∞ 1
k=0 k!
• π = 3.14159265358979323846264338327950288419
∞ k+1
= 4 × k=1 (−1) 2k−1
= 4 × (1 − 13 + 15 − . . .)
There is a nice one-one correspondence between the point
√ (a, b) and the complex number (a + ib). A
complex number can also be written as (r, α), where r = a2 + b2 is the length, and α = atan2(b, a) is
the counter-clockwise angle from the x-axis to the vector. Conversely, if we know (r, α), the point is given
by (r cos(α), ir sin(α)). The product of two complex numbers (r1 , α1 ) and (r2 , α2 ) is (r1 r2 , α1 + α2 ). Based
on these facts, we derive
• sin(α + β) = sin α cos β + sin β cos α
• sin(α − β) = sin α cos β − sin β cos α
• cos(α + β) = cos α cos β − sin α sin β
• cos(α − β) = cos α cos β + sin α sin β
• sin(nθ) = 2 cos θ sin[(n − 1)θ] − sin[(n − 2)θ]
• cos(nθ) = 2 cos θ cos[(n − 1)θ] − cos[(n − 2)θ]
Given any two independent vectors f1 and f2 as basis, we have a coordinate system. If f1 and f2 are
orthogonal unit vectors, we say that they form a orthonormal basis.
For any two vectors u and v, their inner product (u, v) = |u||v| cos(α), where α is the angle between
them. If v is a unit vector, then (u, v) is the projection of u on the v direction.
So, if (e1, e2) is an orthonormal basis, the coordinates of a vector v is just (u, e1 ) for the first coordinate,
(u, e2 ) for the second. This is also true in higher dimensions. In general, if the basis (f1 , f2 ) is not orthonomal
and we want to get the coordinate (a, b) of v in the new system, we just need to solve a simple equation
v = af1 + bf2 , here a and b are unknowns, and we have two equations,
v.x = af1 .x + bf2 .x,
v.y = af1 .y + bf2 .y.
Certainly this has no solution iff (f1 , f2 ) is not a basis, i.e., they are of the same or opposite direction.
Let ABC be a triangle, denote the opposite edges by a, b, and c, respectively, and denote the angles by
α, β, γ, respectively. We have
• α + β + γ = π.
• a = b iff α = β; a < b iff α < β
• a2 = b2 + c2 − 2bc cos α
a b c
• sin α = sin β = sin γ = 2R, where R is the radius of the circumcircle.
• S = 12 ab sin γ = abc 1
4R = 2 (a + b + c)r, where S is the area of the triangle, R is the radius of the
circumcircle, r is the radius of the incircle.
From the fact a2 = b2 + c2 − 2bc cos α, we can derive the following fact: Let a and b be the length of sides
of a parallelogram, x and y be the length of its two diagonals, then 2(a2 + b2 ) = x2 + y 2 .
Our function pAndPoly decides whether a point is inside a simple polygon. If we know the polygon is
convex, then there is an easier program: For each edge, P and the end points of that edge form a triangle.
We add the areas together. P is inside the triangle iff the sum of the areas equals the area of the polygon.
Otherwise, the sum of the areas will be strictly bigger.
46
6 Miscellaneous
6.1 Binary Search
Binary search is done on a monotone array or a monotone function on a range of numbers. The first thing
and most important thing in writing the binary search is to write a good plan, or so called loop invariant.
In the example below, we assume there exists a number x between 0 and n − 1, such that any number
less than x does not have certain property P , and any other number has property P . We want to binary
search the number x.
int binarySearch()
// want: P(lo) is always false, and P(hi) always true
int lo, hi, mi; lo=0; hi=n-1;
while(lo+1<hi)
{
mi=(lo+hi)/2;
if(P(mi)) hi=mi; else lo=mi;
}
return hi;
}
See Problem 7.2 for an nice example of binary search on integers.
In the example below, we assume there exists a real number x between a and b (e.g., 0 and 101 0), such
that we can always tell if a number is smaller than x or not. We want to binary search the number x.
int binarySearch()
// want: lo is always <x, and hi always >=x
double lo, hi, mi; lo=a; hi=b;
while(lo+EPS<hi)
// you need to decide what is the right EPS, 1e-9?
{
mi=(lo+hi)/2.0;
if(mi is too small) lo=mi; else hi=mi;
}
return hi;
}
When playing with big numbers with char arrays, some times we use√ 10-nary search. For example, if we
have a big integer v in a char array, we want to find the integer part of v. A binary search would involve
long integer division, which is a little painful. The 10-nary search is a little slower, but with a succinct
program.
47
For the queries of the form getmax(a,b) asks the maximum elements between r[a] and r[b], inclusive,
the task is little more hard. The idea is always to get the max in some big ranges,√ so in the queries we may
try
√ to use these big ranges
√ to compute fast. One simple algorithm is T (n, n): Break the n numbers into
n regions, each of size n. Compute the champion √ for each region. For each query
√ getmax(a,b), we go
from a towards right to the nearest station (at most n steps), then go by at most n stations (big regions)
to the nearest station before b, and from there go to b. Below we give a nice T (n, log n) algorithm. 1 We
pre-process in log n levels. On level i, all the blocks are of size 2i , and each starting point is divisible by 2i .
int r[50010];
int mm[50010][18]; // or n and log(n) +1
void construct() {
int i,j,b;
for(i=0;i<n;i++) mm[i][0]=r[i];
for(i=1;i<18;i++)
{
for(j=0; (j+(1<<i)-1)<n; j+=(1<<i))
mm[j][i]=max(mm[j][i-1], mm[j+(1<<(i-1))][i-1]);
}
}
48
int pa[30000], rk[30000], tCnt[30000]; // is it big enough?
// parent, rank, and number of nodes in the subtree (if it is the root)
void init(int n)
{
int i;
for(i=0;i<n;i++)
{
pa[i]=i; tCnt[i]=1; rk[i]=0;
}
}
int find(int x)
{
int a=x;
while(pa[a]!=a) a=pa[a];
int b=x,c;
while(pa[b]!=a)
{
c=pa[b];
pa[b]=a;
b=c;
}
return(a);
}
49
void kmpsetup (char *pat, int* f) {
int i, k, len = strlen(pat);
for (f[0] = -1, i = 1; i < len; i++) {
k = f[i-1];
while (k >= 0)
if (pat[k] == pat[i-1]) break; else k = f[k];
f[i] = k + 1;
}
}
/* kmpscan: find substring pat in string text using
back tracing link recorded in f. */
int kmpscan (char *pat, char *text, int *f) {
int i, k, ret = -1, len = strlen(pat);
for (i = k = 0; text[i];) {
if (k == -1) { i++; k = 0; }
else if (text[i] == pat[k]) {
i++; k++;
if (k >= len) { ret = i - len; break; }
} else k = f[k];
}
return ret;
}
int main (void)
{
int r;
scanf ("%s %s", pat, text);
kmpsetup (pat, f);
r = kmpscan (pat, text, f);
if (r == -1)
printf ("Not Found\n");
else
printf ("substring starts at %d\n", r);
}
This kind of problems are very easy when n and m are small (say, 100). One imagines that the original
chessboard is a sheet of clean (white) paper, and those squares covered by some rectangle will become gray.
When a rectangle comes, one just simulate the job of ”cover” the chessboard by setting all the squares in
the region to gray.
Let N and M be the size of the chessboard, and R be the number of rectangles. It becomes a problem
when the size of the chessboard is very large. (e.g., it is bad enough if one merely needs to initialize a
50
2d array of size 10000 × 10000.) However, a basic observation is that the key factor here is R. We think
the (huge) chess board as just the piece of paper, and draw lines according to the edges of the rectangles.
Therefore, the R rectangles only introduces 2R horizontal lines and 2R vertical lines. These lines introduce
a chessboard of size at most 2R × 2R, although each square is no longer a unit square, but a rectangle.
For simplicity of the program, we usually work with a 4R × 4R chessboard, where we do not differentiate
the numbers (coordinates) introduced by the horizontal lines and vertical lines. We call these numbers the
critical numbers.
The above technique works in general when we have problems in two dimensional plane or big arrays
where the critical events are sparse.
Here are the variables we are going to use:
struct REC
{
int x,y,xx,yy; // rectangle from x->xx, y->yy
void input() {cin>>x>>y>>xx>>yy;}
};
REC rec[101];
map<int, int> mp;
vector<int> vt;
int bd[500][500];
rec[] are the rectangles; bd[][] is the chessboard we will play the simulation, vt[] is the vector of
all the critical numbers in increasing order (there might be repeated critical numbers, it do not matter in
general — by doing that we just introduced some empty rectangles); and mp maps a critical number to its
position in vt, i.e., given a coordinate, tell us which row (column) on the chessboard does that coordinate
corresponds to.
We first read all the rectangles and record the critical numbers.
for(i=0;i<R;i++) rec[i].input();
vt.clear();
for(i=0;i<R;i++)
{
vt.push_back(rec[i].x); vt.push_back(rec[i].y);
vt.push_back(rec[i].xx); vt.push_back(rec[i].yy);
}
Then we put a bounding box and sort the coordinates. And build the map from the coordinates to
indices.
vt.push_back(-100000000);
vt.push_back(100000000);
sort(vt.begin(), vt.end());
mp.clear();
for(int c=0; c<vt.size(); c++)
mp[vt[c]]=c;
Now we can play the simulation on the board — initialize the board and put the rectangles. Note that
bd[i][j] records the status of the square from (vt[i], vt[j]) to (vt[i+1], vt[j+1]).
51
for(x=mp[rec[i].x]; x<mp[rec[i].xx]; x++)
for(y=mp[rec[i].y]; y<mp[rec[i].yy]; y++)
bd[x][y]=1;
}
This is the 2D version of the problem in Section 1.4. The bad algorithm runs in O(n6 ) time by (1)
pick all the subarrays (O(n4 )) and (2) sum the numbers inside each array (O(n2 )). By a preprocessing and
inclusion/exclusion, we can reduce it to O(n4 ), where each step (2) only takes O(1) time. (Let s[i][j] be
the sum of all the numbers from the upper left corner to the position (i, j).)
In Section 1.4 we see how to solve the one dimensional max sum problem in O(n) time. We borrow that
to get an O(n3 ) time algorithm to solve Problem 6.2.
(a). Preprocess the sum of vertical segments, s[i][j][k] to be the sum of all the numbers on the k-th
column, from the i-th row to the j-th row. Notice that this takes O()n3 time if you use something like
s[i][j][k]=s[i][j-1][k]+a[j][k].
(b) Now, the task reduces to the 1D max sum. We may fix any possible top edge and bottom edge i and j
(O(n2 ) choices), try to find what is the rectangle with i as the top row, j as the bottom row. This is just
the 1D max sum problem with elements s[i][j][0..M-1].
Remark. In the problem about this kind, you always need to be careful about the boundary conditions, and
issues like whether the coordinates should be off by 1. There are many possible solutions. A good practice
is that always expand a 0 row on the top and a 0 column at the left; and always write down the meaning of
your arrays (especially whether the coordinates are inclusive or exclusive) before you start the code.
Having the previous section in your mind, the problem can be solved in a similar manner in O(n3 ) time,
except both step (a) and (b) are simpler. In (a), s[i][j][k] becomes the indicator whether all the numbers
on the k-th column, from the i-th row to the j-th row are 1. If you want to save space, you can just use
s[i][k] to record the first 0 on the k-th column above the i-th row. (This can be processed in O(n2 ) time.)
In (b), once we fix i and j, it becomes a problem of finding the longest consecutive 1’s in an array of 0’s and
1’s.
From the Guide: In case your O(n3 ) algorithm runs beyond the lifetime of the universe, and you finished
your lunch, and none of your teammates looks more clever than you...
Again, we use s[i][k] to record the first 0 on the k-th column above the i-th row. Now, for any fixed
sea level i, s[i][0..M-1] gives a skyline of Manhattan. s[i][k] is the height of the building of width 1 at
the k-th position. Now we have M buildings, thus M + 1 boundaries. Initially each building k has boundary
k and k + 1. The we sort the buildings, from the highest to the lowest, and union them in that order.
Let f[i] be the friend of boundary i. It records the other end of the piece so far connected to i. (i.e., if
i is the left boundary of a piece, then f[i] is the right boundary.) In the beginning, no piece is added, all
f[i]=i.
52
From the tallest building to the shortest building we add them. When we add a building k, we connect
the piece P1 and P2 , where P1 has k as the right bound, and P2 has k + 1 as its left bound. The new piece
is from the left bound of P1 to the right bound of P2 .
l = f[k]; r = f[k+1];
f[l] = r; f[r] = l;
Since we add the buildings from the higher to the lower ones, we can conclude that the all 1 rectangle
which include (i, k) on the top edge can go as far as l to the left, and r to the right. So the all 1 rectangle
we have in this case is of size (r − l) × s[i][k].
Sample Input:
7
19
10
6
0
Sample Output:
6
6
4
4
Remark. The idea is to simulate the Joseph game fast. In the first pass, all the odd numbers are killed, we
are left with a similar problem with size about half of the original size. We can solve the smaller problem,
and see how to get our answer based on the answer to the smaller problem.
In the program, c[n] is the survivor if there are n person, and we start the game from the first person
(kill the first person); d[n] is the survivor if there are n person, and we start the game from the second
person. Actually one array is enough, but then we need to do some shift back and forth, which is not so
necessary when m = 2.
Sample Solution:
53
int c[500010], d[500010]; int n;
void play() {
int i;
c[1]=d[1]=1;
c[2]=2; d[2]=1;
for(i=3; i<500010; i++)
{
if(i%2)
{
c[i]=d[i/2]*2;
d[i]=c[i/2+1]*2-1;
}
else
{
c[i]=c[i/2]*2;
d[i]=d[i/2]*2-1;
}
}
}
int main() {
play();
while(cin>>n && n) cout<<c[n]<<endl;
}
Problem 7.2 (Basic Numbers) Yijian finished his study in the primary school at the age of thirteen. At
that time he learnt addition, multiplication and division. And he was introduced to the concept of prime
numbers. Like most of us, he likes mathematics, he was so glad that, after one day’s happy work, he was
able to generate the first thirteen prime numbers - a great achievement for a thirteen-year-old boy. However,
unlike most of us, he is not very good at math. Any thing beyond those primes appears too complicated for
Yijian. He never thought about whether there are infinitely many primes; he is satisfied with the ability that,
given any number, he can tell whether or not it is divisible by any of the first thirteen primes.
The first thirteen prime numbers are 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, and 41; their product is
304250263527210. A number called basic if it is divisible by at least one of the first thirteen primes. Thus,
the first number that is not basic is 1, and the second is 43. Yijian write all the basic numbers in ascending
order in a (very long) list. Your task is to find out, given k, what is the k-th element in the list.
The input consists of up to 500 test cases. In each case there is a single number k. For each test
case, output the k-th basic number on a single line. You may assume that the answer is never bigger than
304250263527210.
Sample Input:
2 3 8
2
3 42
Sample Output:
3
4
54
9
3
4
44
Remark. Yijian, my dear friend, is the first person who ever beat me seriously in any mathematical
competition. Clearly anything said about his math ability in the statement is just my opinion.
Remark. The solution combines the binary search and inclusion-exclusion principle. Given a rank k, it is
hard to find the k-th answer. But the inverse is somehow easy: Given a number, we can tell how many basic
numbers are below it by inclusion-exclusion.
Sample Solution:
void init()
{
int s, i;
for(s=1;s<(1<<13);s++)
{
m[s]=1; sg[s]=-1;
for(i=0;i<13;i++) if(s&(1<<i))
{
sg[s]=-sg[s];
m[s]=m[s]*p[i];
}
}
//cout<<m[(1<<13)-1]<<endl;
}
int main()
{
init();
55
long long k,lo,hi,mi,k0;
while(cin>>k)
{
// Do a binary search on play() we get the answer.
lo=1; hi=m[(1<<13)-1]+1;
// assertion: from 1 to lo there will be less than k
// from 1 to hi there will be no less than k
while(lo+1<hi)
{
mi=(lo+hi)/2;
k0=play(mi);
if(k0<k) lo=mi; else hi=mi;
}
cout<<hi<<endl;
}
}
Sample Input:
3
1 1
2 3
3 1
4
1 1
2 3
3 1
4 2
Sample Output:
6.47
7.89
Remark. The problem (a,b) is defined for all a ≤ b, to find the shortest (a, b)-thread, which means two
paths, both starting from the leftmost point 0 and both goes from the left to the right. One of the paths
ends at a, the other at b, and such that each point between 0 and b belongs to at least one of these paths.
Note that we actually consider all the tours where each point is visited at least once, but in the optimal
solution certainly no point will be visited twice.
Sample Solution:
struct PT {
56
double x,y;
};
int main() {
int i,j;
while(cin>>n)
{
pt.resize(n);
for(i=0;i<n;i++) cin>>pt[i].x>>pt[i].y;
for(i=0;i<n;i++) for(j=0;j<n;j++) m[i][j]=-1.0;
m[0][0]=0.0;
for(i=0;i<n;i++)
for(j=i;j<n;j++)
play(i,j);
printf("%.2f\n", m[n-1][n-1]);
}
return 0;
}
Problem 7.4 (the Traveling Salesman on a Small Graph) [UVA 10944] So as Ryan and Larry de-
cided that they don’t really taste so good, they realized that there are some nuts located in certain places of
the island.. and they love them! Since they’re lazy, but greedy, they want to know the shortest tour that they
can use to gather every single nut!
Input: You’ll be given x, and y, both less than 20, followed by x lines of y characters each as a map of the
area, consisting sorely of ”.”, ”#”, and ”L”. Larry and Ryan are currently located in ”L”, and the nuts are
57
represented by ”#”. They can travel in all 8 adjacent direction in one step. See below for an example. There
will be at most 15 places where there are nuts, and ”L” will only appear once.
Output: On each line, output the minimum amount of steps starting from ”L”, gather all the nuts, and
back to ”L”.
Sample Input:
5 5
L....
#....
#....
.....
#....
Sample Output:
8
Remark. The problem will be more interesting if you are not allowed to touch a nut twice. In this case,
the r[s][i] cannot be pre-computed, it also depends on the set S. We would need a shortest path problem
nested in the DP.
Sample Solution:
int N,M;
char bd[50][50];
int x[20],y[20],r[20][20];
int n;
int m[1<<17][17];
int abs(int a)
{
if(a<0) return -a;
return a;
}
int main()
{
int i,j;
while(cin>>N>>M)
58
{
for(i=0;i<N;i++) cin>>bd[i];
n=1;
for(i=0;i<N;i++) for(j=0;j<M;j++)
{
if(bd[i][j]==’L’) {x[0]=i; y[0]=j;}
if(bd[i][j]==’#’) {x[n]=i; y[n]=j; n++;}
}
for(i=0;i<n;i++) for(j=0;j<n;j++)
r[i][j]=max(abs(x[i]-x[j]), abs(y[i]-y[j]));
memset(m, -1, sizeof(m));
cout<<play((1<<n)-1, 0)<<endl;
}
return 0;
}
7.3 Graphs
7.3.1 BFS
Problem 7.5 (Dungeon Master) [UVA 532] Find the shortest path in a 3D maze from ’S’ to ’E’. It is
clear from the sample
Sample Input:
3 4 5
S....
.###.
.##..
###.#
#####
#####
##.##
##...
#####
#####
#.###
####E
1 3 3
S##
#E#
###
0 0 0
Sample Output:
Escaped in 11 minute(s).
Trapped!
59
Sample Solution:
int R, L, C;
char dun[40][40][40];
int d[40][40][40];
int dead, born;
int arrx[64000], arry[64000], arrz[64000];
void bfs() {
int x,y,z,dd;
born=dead=0;
memset(d, -1, sizeof(d));
for(x=0; x<R; x++) for(y=0; y<L; y++) for(z=0; z<C; z++)
if(dun[x][y][z] == ’S’)
{
arrx[born]=x; arry[born]=y; arrz[born]=z;
born++;
d[x][y][z] = 0;
}
while(born>dead)
{
x=arrx[dead]; y=arry[dead]; z=arrz[dead]; dead++;
dd=d[x][y][z];
if(dun[x][y][z] == ’E’)
{
cout<<"Escaped in "<<dd<<" minute(s)."<<endl;
return;
}
play(x+1,y, z, dd); play(x-1, y, z, dd);
play(x,y+1, z, dd); play(x, y-1, z, dd);
play(x,y, z+1, dd); play(x, y, z-1, dd);
}
cout<<"Trapped!"<<endl;
}
int main() {
while(cin>>R>>L>>C && R)
{
for(int i=0; i<R; i++)
for(int j=0; j<L; j++)
cin>>dun[i][j];
bfs();
60
}
}
7.3.2 DFS
Problem 7.6 (Cut Point) [Greater New York ACM/ICPC 2000] We omit the problem statement. The
task is clear from the sample.
Sample Input:
1 2 5 4 3 1 3 2 3 4 3 5 0
1 2 2 3 3 4 4 5 5 1 0
1 2 2 3 3 4 4 6 6 3 2 5 5 1 0
0
Network #1
SPF node 3 leaves 2 subnets
Network #2
No SPF nodes
Network #3
SPF node 2 leaves 2 subnets
SPF node 3 leaves 2 subnets
Sample Solution:
int input() {
int i,j;
for(i=0;i<1010;i++) { eg[i].clear(); v[i]=0; qq[i]=0; }
st=0;
while(1)
{
cin>>i;
if(i==0) break;
cin>>j;
st=i;
if(i==j) continue;
eg[i].insert(j);
eg[j].insert(i);
61
}
return(st);
}
int main() {
int cs=0;
int i,j;
while(input())
{
dfs(st, 0);
if(cs) cout<<endl;
cs++;
cout<<"Network #"<<cs<<endl;
j=0;
for(i=1;i<1010;i++)
if(qq[i]>1)
{
cout<<" SPF node "<<i<<" leaves "<<qq[i]<<" subnets"<<endl;
j=1;
}
if(j==0) cout<<" No SPF nodes"<<endl;
62
}
return(0);
}
Input: The first line of the input contains the number of problems p to be solved.
The first line of every problem contains the number s of streets (1 ≤ s ≤ 50 ), followed by the number a
of avenues (1 ≤ a ≤ 50 ), followed by the number b (b ≥ 1) of robbers.
Then b lines follow, each containing the location of a robber in the form of two numbers x (the number
of the street) and y (the number of the avenue).
Output: The output file consists of p lines. Each line contains the text possible or not possible. If it
is possible to plan non-crossing get-away routes, this line should contain the word: possible. If this is not
possible, the line should contain the words not possible.
Remark. . In the program we define aj as the adjacency list and pc[i] is the counter (size) of aj[i]. We
know in this problem the maximum degree (not in the grid graph, but the actual graph we run the max flow)
is at most 200. If in a problem you do not know any good upper bound of degrees, you may just change aj
to be of type vector<int>.
You always need to estimate the size of the array you need in a problem. Here all the 5010 or 6000 are
upper bounds of the number of vertices. (Again, not on the grid, but the two level graph you will run max
flow.)
Sample Solution:
63
struct edge { int x,y, f,c, rev; };
edge eg[500000];
int aj[5010][200]; int pc[5010];
int phi[5010]; int ex[5010];
int mac[6000]; int ac[5010];
int dead, born;
void push(int a)
{
int x=eg[a].x; int y=eg[a].y; int gg=ex[x];
if(gg>eg[a].c) gg=eg[a].c;
eg[a].f+=gg; eg[a].c-=gg;
int k=eg[a].rev;
eg[k].f-=gg; eg[k].c+=gg;
ex[x]-=gg; ex[y]+=gg;
if(ex[x]==0) {dead=(dead+1)%6000; ac[x]=0;}
if(y && y<N-1 && ac[y]==0) {mac[born]=y; ac[y]=1; born=(born+1)%6000;}
}
int maxflow()
{
int i,j,k,t1,t2,t3;
//for(i=0;i<M;i++) eg[i].f=0;
for(i=1;i<N;i++) { ex[i]=0; ac[i]=0; }
ex[0]=1000000000;
dead=born=0;
for(i=0, j=pc[0];i<j;i++)
push(aj[0][i]);
phi[0]=N;
for(i=1;i<N;i++) phi[i]=0;
while(dead!=born)
{
i=mac[dead];
t2=100000000;
for(t1=pc[i], j=0; j<t1; j++)
{
k=aj[i][j];
if(eg[k].c==0) continue;
t3=phi[eg[k].y]+1;
if(t3<t2) t2=t3;
if(phi[i]==phi[eg[k].y]+1)
{
push(k);
j=t1+10;
}
}
if(j<t1+5) phi[i]=t2;
}
int ans=0;
for(i=0, j=pc[0];i<j;i++)
64
{
k=aj[0][i];
ans+=eg[k].f;
}
//cout<<ans<<endl;
return(ans);
}
void init(int a)
{
int i;
N=a;
for(i=0;i<N;i++) pc[i]=0;
M=0;
}
int n,m;
int B;
int oPt(int a, int b){ return(2*(a*m+b)+1); }
int iPt(int a, int b){ return(2*(a*m+b)+2); }
int main()
{
int i,j,k;
int q; cin>>q;
while(q)
{
q--;
cin>>n>>m;
init(2*m*n+2);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
k=oPt(i,j);
addEdge(iPt(i,j),k,1);
if(i==0) addEdge(k,N-1,1);
else addEdge(k,iPt(i-1,j),1);
if(i==n-1) addEdge(k,N-1,1);
else addEdge(k,iPt(i+1,j),1);
65
if(j==0) addEdge(k,N-1,1);
else addEdge(k,iPt(i,j-1),1);
if(j==m-1) addEdge(k,N-1,1);
else addEdge(k,iPt(i,j+1),1);
}
cin>>B;
for(k=0;k<B;k++)
{
cin>>i>>j;
i--;j--;
if(B<=200) addEdge(0,iPt(i,j),1);
}
if(B>200) cout<<"not possible";
else if(maxflow()==B) cout<<"possible";
else cout<<"not possible";
cout<<endl;
}
return(0);
}
Problem 7.8 (Going Home) On a grid map there are n little men and n houses. In each unit time, every
little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little
man, you need to pay $1 travel fee for every step he moves, until he enters a house. The task is complicated
with the restriction that each house can accommodate only one little man.
Your task is to compute the minimum amount of money you need to pay in order to send these n little
men into those n different houses. The input is a map of the scenario, a ’.’ means an empty space, an ’H’
represents there is a house on that point, and am ’m’ indicates there is a little man on that point.
You can think of each point on the grid map is a quite large square, so it can hold n little men at the
same time; also, it is okay if a little man steps on a grid with a house without entering that house.
Sample Input:
2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
66
...H....
...H....
...H....
Sample Output:
2
10
28
In the ACM/ICPC world final 2005, the legendary Robert Renaud solve a hard problem, part of the
program uses the min cost flow code in the notes. The note, prepared by the legendary Ben Etin, is actually
the max cost flow. Robert spent a long time and discovered this. The difference between max/min cost is
just changing one ’<’ to ’>’ in the code. Be aware! I believe this note is correct, and the necessary change
is commented in the code.
Sample Solution:
int augCycle()
{
// Find a positive cycle in the Delta graph.
int i,j,k;
int dt[110][110]; // Delta: Edge weights in the Augumenting Graph
int fs[110][110]; // Parent link for the biggest paths
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{
dt[i][j]=Cost[i][mc[i]]-Cost[i][mc[j]];
fs[i][j]=j;
}
// Floyd
for(k=0;k<N;k++)
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(dt[i][k]+dt[k][j]>dt[i][j])
// !!!! change to < if want max cost matching
{
dt[i][j]=dt[i][k]+dt[k][j];
67
fs[i][j]=fs[i][k];
if(i==j)
{
cccnt=0;
do
{
cc[cccnt]=i; cccnt++;
i=fs[i][j];
}while(i!=j);
return(1);
}
}
return(0);
}
int bestMatching()
{
int i,j;
for(i=0;i<N;i++) mc[i]=i;
while(augCycle())
{
j=mc[cc[0]];
for(i=0;i<cccnt-1;i++)
mc[cc[i]]=mc[cc[i+1]];
mc[cc[i]]=j;
}
int ret=0;
for(i=0;i<N;i++) ret+=Cost[i][mc[i]];
return(ret);
}
char Map[40][40];
int main()
{
int n,m,i,j,x,y,t1,t2;
while(cin>>n>>m)
{
for(i=0; i<n; i++) cin>>Map[i];
N = 0;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
if(Map[i][j] == ’H’) N++;
t1 = -1;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
if(Map[i][j] == ’H’)
{
t1++; t2 = -1;
for(x=0; x<n; x++)
for(y=0; y<m; y++)
68
if(Map[x][y] == ’m’)
{
t2++;
Cost[t1][t2] = abs(i - x) + abs(j - y);
}
}
cout<<bestMatching()<<endl;
}
return(0);
}
Sample Solution:
void init(int a)
{
N=a; M=0;
int i,j;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
f[i][j]=c[i][j]=0;
}
69
fg=0;
for(k=0;k<M;k++)
{
i=ex[k]; j=ey[k];
if(f[i][j]<c[i][j])
{
if(f[i][j]<0) ww=-w[k]; else ww=w[k];
if(dist[j]>dist[i]+ww)
{
dist[j]=dist[i]+ww;
aug[j]=min(aug[i], c[i][j]-f[i][j]);
pa[j]=i;
fg=1;
}
}
}
}while(fg);
if(dist[N-1]>=infty/2) return(0);
int wk=N-1,wk1;
while(wk)
{
wk1=pa[wk];
f[wk1][wk]+=aug[N-1];
f[wk][wk1]-=aug[N-1];
wk=wk1;
}
return(dist[N-1]*aug[N-1]);
}
char Map[40][40];
70
int main()
{
int n,m,i,j,x,y,t,t1,t2,cst;
while(cin>>n>>m)
{
for(i=0; i<n; i++) cin>>Map[i];
t = 0;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
if(Map[i][j] == ’H’) t++;
init(2*t+2);
t1 = 0;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
if(Map[i][j] == ’H’)
{
t1++; t2 = t;
for(x=0; x<n; x++)
for(y=0; y<m; y++)
if(Map[x][y] == ’m’)
{
t2++;
cst = abs(i - x) + abs(j - y);
addEdge(t1,t2,1,cst);
addEdge(t2,t1,1,cst);
}
}
for(i=1; i<=t; i++)
{
addEdge(0,i,1,0);
addEdge(i,0,1,0);
}
for(i=t+1; i<=2*t; i++)
{
addEdge(2*t+1,i,1,0);
addEdge(i,2*t+1,1,0);
}
cout<<minCostFlow()<<endl;
}
return(0);
}
Input: The first line of input contains a single positive integer. This is the number of lines that follow.
Each of the following lines will have a (decimal) input base followed by a (decimal) output base followed by
71
a number expressed in the input base. Both the input base and the output base will be in the range from 2
to 62. That is (in decimal) A = 10, B = 11, · · · , Z = 35, a = 36, b = 37, · · · , z = 61 (0-9 have their usual
meanings).
Sample Input:
3
62 2 abcdefghiz 10 16 1234567890123456789012345678901234567890 16
35 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
Sample Output:
62 abcdefghiz
2 1011100000100010111110010010110011111001001100011010010001
10 1234567890123456789012345678901234567890
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 333YMHOUE8JPLT7OX6K9FYCQ8A
Sample Solution:
72
s[i]=’0’+d;
}
while(c)
{
s[i]=’0’+(c%b); i++;
c/=b;
}
s[i]=’\0’;
}
int main() {
int q; cin>>q;
int i,j;
while(q)
{
q--;
cin>>a>>b>>sa; sb[0]=’0’; sb[1]=’\0’;
cout<<a<<" "<<sa<<endl;
trans(sa);
for(i=0;sa[i];i++)
{
multi(sb, a);
add(sb, sa[i]-’0’);
}
rev(sb);
itrans(sb);
cout<<b<<" "<<sb<<endl;
cout<<endl;
}
return 0;
}
73
Index
augmenting path, 17 max bipartite matching, 17, 18, 20
lexicographically smallest, 18
Bellman-Ford, 9, 22 max flow, 19, 63
best triangulation, 3 integer, 19
BFS, 5, 20, 59 with capacity on vertices, 20
binary search, 47, 55 Max Flow Min Cut Theorem, 19
bipartite graph, 17 max sum
one dimensional, 3
Cao, Yijian, 54 two dimensional, 52
Cayley’s Formula, 15 method of relaxation, 34
connected components, 6 min cost bipartite matching, 21, 22, 66
strongly, 7 min cost flow, 22, 66
cut point, 9, 61 min cut, 19, 21
cycle canceling, 21, 67 minimum average cycle, 14
minimum spanning tree, 15
determinant, 31
DFS, 6, 8, 9, 18, 61 negative cycle, 9, 10
tree, 7
Dijkstra, 10, 15 preflow-push-relabel, 20
Prim, 15
edge-disjoint paths, 20 prime test, 23, 25
Edmonds-Karp, 20
electric network, 34 random walk, 34
Euler’s Φ function, 23 range query, 47
Euler’s Theorem, 23 rectangles in the plane, 50
Renaud, Robert, 31, 67
Farach-Colton, Martin, 48 repeated squaring, 24, 31
Fermat’s Little Theorem, 22 residue network, 20
find and union, 17, 48
Floyd-Warshall, 10 shortest path, 9, 10
DAG, 12
greatest common divisor, 23 lexicographically smallest, 12
extended, 23 within certain number of steps, 13
sieve of Erastothenes, 23
Hamilton cycle, 5
string matching, 49
inclusion-exclusion, 55 subset sum, 1
successive shortest path, 22, 69
Joseph problem, 53 system of linear equations, 31, 34
74