(Analysis by Benjamin Qi)

Let's start by constructing a directed graph $G$ with vertices labeled $1\ldots N$ that contains an edge $i\to j$ if $i=j$ or cow $i$ prefers gift $j$ to gift $i$. For the sample case, $G$ 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 $G$ 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 $G$'s edges: $\{1\to 1, 2\to 3, 3\to 2, 4\to 4\}$. This subset of edges partitions the vertices of $G$ into three cycles: a self-loop involving $1$, a loop involving $2$ and $3$, and another self-loop involving $4$.

Observation 1: There is a distribution where cow $i$ receives gift $j$ if and only if $G$ has a simple cycle containing edge $i\to j$.

Proof:

Only If: A distribution where cow $i$ receives cow $j$ corresponds to a partition of the vertices of $G$ into some number of simple cycles containing the edge $i\to j$. It follows that one of those simple cycles contains the edge $i\to j$.

If: Suppose there exists a simple cycle $C$ containing $i\to j$. Then we can assign each cow along $C$ the gift originally given to the next cow along the cycle, and every cow along $C$ will end up strictly better off. Let all cows not along $C$ receive their original gifts. This corresponds to a valid distribution. $\blacksquare$

Observation 2: Using the first observation, $G$ has a simple cycle containing edge $i\to j$ if and only if there exists a path from $j$ to $i$.

Solution: In $\mathcal{O}(N^3)$ time, compute all pairs of vertices $(i,j)$ such that there exists a path from $i$ to $j$. In the code below, we set $\texttt{reachable}[i][j]=1$ if such a path exists. The most straightforward way to do this is to start a depth-first search from each $i$. Alternatively, we can use Floyd-Warshall with bitsets to shave a constant factor off the runtime.

After that, for each cow $i$, it remains to iterate over her preference list and output the first gift $g$ such that there exists a path from $g$ to $i$.

#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: