문제

총 N명의 사람이 책을 구매하려고 한다. 각 사람은 1번부터 N번까지 번호가 매겨져 있고, 각 사람이 사려고하는 책의 개수는 A1, A2, ..., AN권이다. 이 책을 판매하는 온라인 서점은 총 M곳이 있다.각 서점도 1번부터 M번까지 번호가 매겨져 있으며, 각 서점이 가지고 있는 책의 개수는 B1, B2, ..., BM권 이다.

이 책을 사려고 하는 사람은 N명밖에 없으며, 서점에서 가지고 있는 책의 개수의 합과 사람들이 사려고 하는 책의 개수의 합은 같다.

한 사람이 같은 서점에서 구매할 수 있는 책의 개수는 제한되어 있다. 사람 j가 서점 i에서 구매할 수 있는 책의 최대 개수는 Cij개이다. 모든 서점과 사람 사이의 구매 제한이 주어졌을 때, 책을 최대 몇 개 살 수 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 사람의 수 N과 온라인 서점의 수 M이 주어진다. (1 ≤ N, M ≤ 100)

둘째 줄에는 각 사람이 사려고 하는 책의 개수 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100)

셋째 줄에는 각 온라인 서점이 가지고 있는 책의 개수 B1, B2, ..., BM이 주어진다. (1 ≤ Bi ≤ 100)

넷째 줄부터 M개의 줄에는 각 온라인 서점이 각 사람들에게 책을 최대 몇 권 팔 수 있는지를 나타내는 Cij가 주어진다. i번째 줄의 j번째 숫자는 온라인 서점 i에서 사람 j는 책을 최대 Cij권 살 수 있다는 의미이다. (0 ≤ Cij ≤ 100)

출력

첫째 줄에 살 수 있는 책의 최대 개수를 출력한다.

 

입력

4 4
3 2 4 2
5 3 2 1
0 1 1 0
1 0 1 2
2 1 1 1
0 0 2 0

출력

8

 

구현


        
import java.util.*;
class MaximumFlow {
    class Edge {
        int to, capacity;
        Edge rev;
        Edge(int to, int capacity) {
            this.to = to;
            this.capacity = capacity;
        }
    }
    int n, source, sink;
    ArrayList<Edge>[] graph;
    boolean[] check;
    MaximumFlow(int n, int source, int sink) {
        this.n = n;
        this.source = source;
        this.sink = sink;
        graph = new ArrayList[n];
        check = new boolean[n];
        for (int i=0; i<n; i++) {
            graph[i] = new ArrayList<Edge>();
        }
    }
    void add_edge(int u, int v, int cap) {
        Edge ori = new Edge(v, cap);
        Edge rev = new Edge(u, 0);
        ori.rev = rev;
        rev.rev = ori;
        graph[u].add(ori);
        graph[v].add(rev);
    }
    void add_edge_from_source(int v, int cap) {
        add_edge(source, v, cap);
    }
    void add_edge_to_sink(int u, int cap) {
        add_edge(u, sink, cap);
    }
    int bfs() {
        boolean[] check = new boolean[n];
        int[] from1 = new int[n];
        int[] from2 = new int[n];
        for (int i=0; i<n; i++) {
            from1[i] = from2[i] = -1;
        }
        Queue<Integer> q = new LinkedList<>();
        q.add(source);
        check[source] = true;
        while (!q.isEmpty()) {
            int x = q.remove();
            for (int i=0; i<graph[x].size(); i++) {
                Edge e = graph[x].get(i);
                if (e.capacity > 0 && !check[e.to]) {
                    q.add(e.to);
                    check[e.to] = true;
                    from1[e.to] = x;
                    from2[e.to] = i;
                }
            }
        }
        if (!check[sink]) {
            return 0;
        }
        int x = sink;
        int c = graph[from1[x]].get(from2[x]).capacity;
        while (from1[x] != -1) {
            if (c > graph[from1[x]].get(from2[x]).capacity) {
                c = graph[from1[x]].get(from2[x]).capacity;
            }
            x = from1[x];
        }
        x = sink;
        while (from1[x] != -1) {
            Edge e = graph[from1[x]].get(from2[x]);
            e.capacity -= c;
            e.rev.capacity += c;
            x = from1[x];
        }
        return c;
    }
    int flow() {
        int ans = 0;
        while (true) {
            Arrays.fill(check, false);
            int f = bfs();
            if (f == 0) break;
            ans += f;
        }
        return ans;
    }
}
public class Main {
    public static void main(String args[]) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        MaximumFlow mf = new MaximumFlow(n+m+2, n+m, n+m+1);
        for (int i=0; i<n; i++) {
            int x = sc.nextInt();
            mf.add_edge_to_sink(m+i,x);
        }
        for (int i=0; i<m; i++) {
            int x = sc.nextInt();
            mf.add_edge_from_source(i,x);
        }
 
        for (int i=0; i<m; i++) {
            for (int j=0; j<n; j++) {
                int x = sc.nextInt();
                mf.add_edge(i,j+m,x);
            }
        }
        System.out.println(mf.flow());
    }
}