#include #include #include #include #include #include #include #include using Matrix = std::vector>; double pure_greedy(const Matrix& a, int& best_row) { double best = -std::numeric_limits::infinity(); for (int i = 0; i < static_cast(a.size()); ++i) { const double value = *std::min_element(a[i].begin(), a[i].end()); if (value > best) { best = value; best_row = i; } } return best; } // Intentionally incorrect: fixed-iteration simulated annealing has no // guarantee of reaching the accuracy required by the problem. double solve(const Matrix& a, std::uint64_t seed) { constexpr int iterations = 200000; const int n = static_cast(a.size()); const int m = static_cast(a[0].size()); if (n == 1) return *std::min_element(a[0].begin(), a[0].end()); std::vector probability(n, 1.0 / n); std::vector payoff(m, 0.0); for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) payoff[j] += probability[i] * a[i][j]; double current = *std::min_element(payoff.begin(), payoff.end()); double best = current; int greedy_row = 0; const double greedy_value = pure_greedy(a, greedy_row); if (greedy_value > current) { std::fill(probability.begin(), probability.end(), 0.0); probability[greedy_row] = 1.0; payoff = a[greedy_row]; current = greedy_value; best = current; } std::mt19937_64 rng(seed); std::uniform_real_distribution real01(0.0, 1.0); std::uniform_int_distribution index(0, n - 1); std::vector candidate(m); for (int iteration = 0; iteration < iterations; ++iteration) { int source = index(rng); for (int retry = 0; retry < 20 && probability[source] < 1e-15; ++retry) source = index(rng); if (probability[source] < 1e-15) continue; int destination = index(rng); if (destination == source) destination = (destination + 1) % n; const double progress = static_cast(iteration) / (iterations - 1); const double maximum_move = 0.5 * std::pow(2e-7, progress); const double delta = std::min(probability[source], maximum_move) * real01(rng); double next = std::numeric_limits::infinity(); for (int j = 0; j < m; ++j) { candidate[j] = payoff[j] + delta * (a[destination][j] - a[source][j]); next = std::min(next, candidate[j]); } const double temperature = 500.0 * std::pow(1.0 - progress, 3) + 1e-12; if (next >= current || real01(rng) < std::exp((next - current) / temperature)) { probability[source] -= delta; probability[destination] += delta; payoff.swap(candidate); current = next; best = std::max(best, current); } } return best; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int tests; std::cin >> tests; std::cout << std::fixed << std::setprecision(15); for (int test = 0; test < tests; ++test) { int n, m; std::cin >> n >> m; Matrix a(n, std::vector(m)); for (auto& row : a) for (double& value : row) std::cin >> value; std::cout << solve(a, 123456789ULL + test) << '\n'; } }