ECR160-E Matrix Problem

問題

https://codeforces.com/contest/1913/problem/E (2400diff)

 n m列の01行列 aが与えられる。また、長さがそれぞれ n, mの数列 A, Bが与えられる。

あなたの目標は aのいくつかのマスを反転することで、  i行目の 1の数を A_{i}個、 j列目の 1の数を B_{j}にすることである。

目標を達成することが可能なら、反転するマスの最小個数を、不可能ならそのことを報告せよ。

制約

  •  1\ \le\ n, m\ \le\ 50

解法

 \sum A = \sum Bが明らかに必要。

最小費用流の問題に帰着させる。各マスに0か1を割り当てることを考える。

  • もともとのマスの値が 0のとき、 0を割り当てるのにコスト 0,  1を割り当てるのにコスト 1
  • もともとのマスの値が 1のとき、「あらかじめ一回操作をして値を 0にした」と考える。 0を割り当てるのにコスト 0,  1を割り当てるのにコスト -1

負コストがあるので、初回のポテンシャル計算にはベルマンフォードを使う。最初ネットワークがDAGなので、dpをしても良い。

実装

#include <bits/stdc++.h>

#include "Src/Template/IOSetting.hpp"
#include "Src/Graph/Flow/SuccessiveShortestPath.hpp"
using namespace zawa;

int main() {
    SetFastIO();

    int n, m; std::cin >> n >> m;
    std::vector s(n, std::vector<int>(m));
    for (auto& v : s) for (auto& x : v) std::cin >> x;
    std::vector<int> a(n), b(m);
    for (auto& x : a) std::cin >> x;
    for (auto& x : b) std::cin >> x;
    int sum{std::reduce(a.begin(), a.end(), 0)};
    if (sum != std::reduce(b.begin(), b.end(), 0)) {
        std::cout << -1 << '\n';
        return 0;
    }
    SuccessiveShortestPath<int, int> mcf(n + m + 2);
    int source{n + m}, sink{source + 1};
    for (int i{} ; i < n ; i++) {
        mcf.addEdge(source, i, a[i], 0);
    }
    for (int i{} ; i < m ; i++) {
        mcf.addEdge(n + i, sink, b[i], 0);
    }
    int ans{};
    for (int i{} ; i < n ; i++) {
        for (int j{} ; j < m ; j++) {
            if (s[i][j] == 0) {
                mcf.addEdge(i, n + j, 1, 1);
            }
            else if (s[i][j] == 1) {
                ans++;
                mcf.addEdge(i, n + j, 1, -1); 
            }
            else {
                assert(false);
            }
        }
    }
    mcf.dagdp(source, sink);
    mcf.updatePotential();
    int flow{mcf.maxflow(source, sink)};
    assert(flow <= sum);
    if (flow < sum) {
        std::cout << -1 << '\n';
        return 0;
    }
    ans += mcf.minCost();
    std::cout << ans << '\n';
}

自分のMCFはACLより遅いが、ベルマンフォードやDAG上のDPでポテンシャルが計算できるのが強み(自慢)

反省

バチャ中はE問題を開くことが無かった。

自分にとってはDよりEの方が解きやすそうに見えた。

しかし、Dが解けていないなかでEのフローを信じて詰めれたかと言われると、かなり自信が無い。Dを解かないと始まらないな。