PROBLEM:
(ENGLISH VERSION)
Bessie and her friends are playing hoofball in the annual Superbull championship, and Farmer John is in charge of making the tournament as exciting as possible. A total of N (1 <= N <= 2000) teams are playing in the Superbull. Each team is assigned a distinct integer team ID in the range 1...2^30-1 to distinguish it from the other teams. The Superbull is an elimination tournament -- after every game, Farmer John chooses which team to eliminate from the Superbull, and the eliminated team can no longer play in any more games. The Superbull ends when only one team remains.
Farmer John notices a very unusual property about the scores in matches! In any game, the combined score of the two teams always ends up being the bitwise exclusive OR (XOR) of the two team IDs. For example, if teams 12 and 20 were to play, then 24 points would be scored in that game, since 01100 XOR 10100 = 11000.
Farmer John believes that the more points are scored in a game, the more exciting the game is. Because of this, he wants to choose a series of games to be played such that the total number of points scored in the Superbull is maximized. Please help Farmer John organize the matches.
INPUT FORMAT: (file superbull.in)
The first line contains the single integer N. The following N lines contain the N team IDs.
OUTPUT FORMAT: (file superbull.out)
Output the maximum possible number of points that can be scored in the Superbull.
SAMPLE INPUT:
4 3 6 9 10
SAMPLE OUTPUT:
37
OUTPUT DETAILS: One way to achieve 37 is as follows: FJ matches teams 3 and 9, and decides that 9 wins, so teams 6, 9, and 10 are left in the tournament. He then matches teams 6 and 9, and lets team 6 win. Teams 6 and 10 are then left in the tournament. Finally, teams 6 and 10 face off, and team 10 wins. The total number of points scored is (3 XOR 9) + (6 XOR 9) + (6 XOR 10) = 10 + 15 + 12 = 37.
NOTE: The bitwise exclusive OR operation, commonly denoted by the ^ symbol, is a bitwise operation that performs the logical exclusive OR operation on each position in two binary integers. The result in each position is 1 if only the first bit is 1 or only the second bit is 1, but is 0 if both bits are 0 or both are 1. For example: 10100 (decimal 20) XOR 01100 (decimal 12) = 11000 (decimal 24)
[Problem credits: Alex Yang, 2015]
USACO SOLUTION:
Let us reformulate this problem as a graph problem - we have N vertices, and every pair of vertices has an edge between them with some weight. All vertices start out being uncolored - in a given turn, we pick an edge between two uncolored vertices and we must color one of the vertices - we can pick which vertex to color. Our goal is to maximize the sum of the weights of the edges that we choose.(本题的目的就是要使得选出的边的权值之和最大) Note that this sounds similar to Kruskal's minimum spanning tree algorithm.
Indeed we can observe that the final tournament structure forms a tree over all teams with the sum of edge weights giving the number of points scored. Therefore this problem is equivalent to the maximum spanning tree problem which is the same as the minimum spanning tree problem with negated edge weights.
Because the graph is dense it is preferable to use Prim's algorithm for computing the maximum spanning tree.(这是一个稠密图,所以用Prim算法更方便) Note that we should use the O(V^2) version of Prim's rather than the version that makes use of a heap. Kruskal's algorithm, on the other hand, requires the edges to be sorted by edge weight which can be too computationally intensive for some implementations(由于Kruskal算法要对权值进行排序,所以这就会导致计算量太大而超时).
Because edge weights can be very large, the answer must be stored in a 64-bit integer.
Here is Mark Gordon's code implementing Prim's algorithm:
![]()
View Code1 #include <bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 freopen("superbull.in", "r", stdin); 6 freopen("superbull.out", "w", stdout); 7 int N; 8 cin >> N; 9 vector<int> A(N); 10 for (int i = 0; i < N; i++) { 11 cin >> A[i]; 12 } 13 long long result = 0; 14 vector<bool> used(N, false); 15 vector<int> D(N, 0); 16 for (int i = 0; i < N; i++) 17 { 18 /* Find the index, j, of the 'furthest' node from the current spanning tree. */ 19 int j = -1; 20 for (int k = 0; k < N; k++) 21 { 22 if (used[k]) 23 continue; 24 if (j == -1 || D[k] > D[j]) 25 j = k; 26 } 27 /* Update the result and 'relax' the edges out of vertex j. */ 28 result += D[j]; 29 used[j] = true; 30 for (int k = 0; k < N; k++) 31 D[k] = max(D[k], A[j] ^ A[k]); 32 } 33 cout << result << endl; 34 return 0; 35 }
分析:
想到利用MST就很容易了。当然因为是要求最大的分数,所以把算法中的语句稍加修改,达到“最大生成树”的效果。在这里显然用Prim算法更为方便。(原因见上)
1 for (int i = 0; i < N; i++) 2 { 3 int j = -1; 4 for (int k = 0; k < N; k++) 5 { 6 if (used[k]) 7 continue; 8 if (j == -1 || D[k] > D[j]) 9 j = k; 10 } 11 result += D[j]; 12 used[j] = true; 13 for (int k = 0; k < N; k++) 14 D[k] = max(D[k], A[j] ^ A[k]); 15 }
这是Prim算法的核心部分。第1行开始的第一重循环先扫过所有的点,分别以每一个点作为起始点计算。第4~9行先找出与当前点临近且分数最高(权值最大)的点。(在Prim中这里通常是找最小的点,那么因为要求最大得分,所以把小于号改称大于号)第11~12行累加最大权值,并且将这个点设置为已处理。然后在第13~14行计算对于当前点i的数组D的值,先计算出A[j]和A[k]的异或值,也就是这两支队伍进行比赛的得分,然后与D[k]做比较,取最大值。这样数组D中存储的就是这个点到临近点的最大权值。
至此我们算出了这张图的最大生成树。
返回输出result就是我们要的答案。