Farmer John has NN gifts labeled 1…N1…N for his NN cows, also labeled 1…N1…N (1≤N≤5001≤N≤500). Each cow has a wishlist, which is a permutation of all NN gifts such that the cow prefers gifts that appear earlier in the list over gifts that appear later in the list.
FJ was lazy and just assigned gift ii to cow ii for all ii. Now, the cows have gathered amongst themselves and decided to reassign the gifts such that after reassignment, every cow ends up with the same gift as she did originally, or a gift that she prefers over the one she was originally assigned.
For each ii from 11 to NN, compute the most preferred gift cow ii could hope to receive after reassignment.
The first line contains NN. The next NN lines each contain the preference list of a cow. It is guaranteed that each line forms a permutation of 1…N1…N.
Please output NN lines, the ii-th of which contains the most preferred gift cow ii could hope to receive after reassignment.
4 1 2 3 4 1 3 2 4 1 2 3 4 1 2 3 4
1 3 2 4
In this example, there are two possible reassignments:
Observe that both cows 11 and 44 cannot hope to receive better gifts than they were originally assigned. However, both cows 22 and 33 can.
Problem credits: Benjamin Qi
[/hide]
(Analysis by Benjamin Qi)
Let's start by constructing a directed graph GG with vertices labeled 1…N1…N that contains an edge i→ji→j if i=ji=j or cow ii prefers gift jj to gift ii. For the sample case, GG would contain the following edges:
1 -> 1 2 -> 1 2 -> 3 2 -> 2 3 -> 1 3 -> 2 3 -> 3 4 -> 1 4 -> 2 4 -> 3 4 -> 4
There is a one-to-one correspondence between valid distributions and partitions of the vertices of GG into simple cycles. For example, assigning gift 1 to cow 1, gift 3 to cow 2, gift 2 to cow 3, and gift 4 to cow 4 would correspond to the following subset of GG's edges: {1→1,2→3,3→2,4→4}{1→1,2→3,3→2,4→4}. This subset of edges partitions the vertices of GG into three cycles: a self-loop involving 11, a loop involving 22 and 33, and another self-loop involving 44.
Observation 1: There is a distribution where cow ii receives gift jj if and only if GG has a simple cycle containing edge i→ji→j.
Proof:
Only If: A distribution where cow ii receives cow jj corresponds to a partition of the vertices of GG into some number of simple cycles containing the edge i→ji→j. It follows that one of those simple cycles contains the edge i→ji→j.
If: Suppose there exists a simple cycle CC containing i→ji→j. Then we can assign each cow along CC the gift originally given to the next cow along the cycle, and every cow along CC will end up strictly better off. Let all cows not along CC receive their original gifts. This corresponds to a valid distribution. ■◼
Observation 2: Using the first observation, GG has a simple cycle containing edge i→ji→j if and only if there exists a path from jj to ii.
Solution: In O(N3)O(N3) time, compute all pairs of vertices (i,j)(i,j) such that there exists a path from ii to jj. In the code below, we set reachable[i][j]=1reachable[i][j]=1 if such a path exists. The most straightforward way to do this is to start a depth-first search from each ii. Alternatively, we can use Floyd-Warshall with bitsets to shave a constant factor off the runtime.
After that, for each cow ii, it remains to iterate over her preference list and output the first gift gg such that there exists a path from gg to ii.
#include <bits/stdc++.h> using namespace std; int N; bitset<501> reachable[501]; vector<int> gifts[501]; void dfs(int src, int cur) { if (reachable[src][cur]) return; reachable[src][cur] = true; for (int g : gifts[cur]) dfs(src, g); } void calc_reachable_dfs() { for (int i = 1; i <= N; ++i) dfs(i, i); } void calc_reachable_floyd() { for (int i = 1; i <= N; ++i) for (int g : gifts[i]) reachable[i][g] = true; for (int k = 1; k <= N; ++k) // run floyd-warshall for (int i = 1; i <= N; ++i) if (reachable[i][k]) reachable[i] |= reachable[k]; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> N; assert(N <= 500); for (int i = 1; i <= N; ++i) { gifts[i].resize(N); for (int &g : gifts[i]) cin >> g; while (gifts[i].back() != i) gifts[i].pop_back(); } // either of these work calc_reachable_dfs(); // calc_reachable_floyd(); for (int i = 1; i <= N; ++i) for (int g : gifts[i]) if (reachable[g][i]) { cout << g << "\n"; break; } }
Danny Mittal's code:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.StringJoiner; import java.util.StringTokenizer; public class RedistributingGifts { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine()); int[][] rankings = new int[n + 1][n + 1]; for (int cow = 1; cow <= n; cow++) { StringTokenizer tokenizer = new StringTokenizer(in.readLine()); for (int rank = n; rank > 0; rank--) { rankings[cow][Integer.parseInt(tokenizer.nextToken())] = rank; } } boolean[][] reachable = new boolean[n + 1][n + 1]; for (int cow1 = 1; cow1 <= n; cow1++) { for (int cow2 = 1; cow2 <= n; cow2++) { if (rankings[cow1][cow2] >= rankings[cow1][cow1]) { reachable[cow2][cow1] = true; } } } for (int cow2 = 1; cow2 <= n; cow2++) { for (int cow1 = 1; cow1 <= n; cow1++) { for (int cow3 = 1; cow3 <= n; cow3++) { reachable[cow1][cow3] = reachable[cow1][cow3] || (reachable[cow1][cow2] && reachable[cow2][cow3]); } } } StringJoiner joiner = new StringJoiner("\n"); for (int cow = 1; cow <= n; cow++) { int bestGift = 0; for (int gift = 1; gift <= n; gift++) { if (rankings[cow][gift] > rankings[cow][bestGift] && reachable[cow][gift]) { bestGift = gift; } } joiner.add(bestGift + ""); } System.out.println(joiner); } }
Additional Notes:
[/hide]
翰林课程体验,退费流程快速投诉邮箱: yuxi@linstitute.net 沪ICP备2023009024号-1