/* * Network Flow * * Author: Howard Cheng * * The routine network_flow() finds the maximum flow that can be * pushed from the source (s) to the sink (t) in a flow network * (i.e. directed graph with capacities on the edges). The maximum * flow is returned. Note that the graph is modified. If you wish to * recover the flow on an edge, it is in the "flow" field, as long as * is_real is set to true. * * Note: if you have an undirected network. simply call add_edge twice * with an edge in both directions (same capacity). Note that 4 edges * will be added (2 real edges and 2 residual edges). To discover the * actual flow between two vertices u and v, add up the flow of all * real edges from u to v and subtract all the flow of real edges from * v to u. (In fact, for a residual edge the flow is always 0 in this * implementation.) * * This code can also be used for bipartite matching by setting up an * appropriate flow network. * * The code here assumes an adjacency list representation since most * problems requiring network flow have sparse graphs. * * This is the basic augmenting path algorithm and it is not the most * efficient. But it should be good enough for most programming contest * problems. The complexity is O(f m) where f is the size of the flow * and m is the number of edges. This is good if you know that f * is small, but can be exponential if f is large. * */ #include #include #include #include #include using namespace std; struct Edge; typedef list::iterator EdgeIter; struct Edge { int to; int cap; int flow; bool is_real; EdgeIter partner; Edge(int t, int c, bool real = true) : to(t), cap(c), flow(0), is_real(real) {}; int residual() const { return cap - flow; } }; struct Graph { list *nbr; int num_nodes; Graph(int n) : num_nodes(n) { nbr = new list[num_nodes]; } ~Graph() { delete[] nbr; } // note: this routine adds an edge to the graph with the specified capacity, // as well as a residual edge. There is no check on duplicate edge, so it // is possible to add multiple edges (and residual edges) between two // vertices void add_edge(int u, int v, int cap) { nbr[u].push_front(Edge(v, cap)); nbr[v].push_front(Edge(u, 0, false)); nbr[v].begin()->partner = nbr[u].begin(); nbr[u].begin()->partner = nbr[v].begin(); } }; void push_path(Graph &G, int s, int t, const vector &path, int flow) { for (int i = 0; s != t; i++) { if (path[i]->is_real) { path[i]->flow += flow; path[i]->partner->cap += flow; } else { path[i]->cap -= flow; path[i]->partner->flow -= flow; } s = path[i]->to; } } // the path is stored in a peculiar way for efficiency: path[i] is the // i-th edge taken in the path. int augmenting_path(const Graph &G, int s, int t, vector &path, vector &visited, int step = 0) { if (s == t) { return -1; } for (EdgeIter it = G.nbr[s].begin(); it != G.nbr[s].end(); ++it) { int v = it->to; if (it->residual() > 0 && !visited[v]) { path[step] = it; visited[v] = true; int flow = augmenting_path(G, v, t, path, visited, step+1); if (flow == -1) { return it->residual(); } else if (flow > 0) { return min(flow, it->residual()); } } } return 0; } // note that the graph is modified int network_flow(Graph &G, int s, int t) { vector visited(G.num_nodes); vector path(G.num_nodes); int flow = 0, f; do { fill(visited.begin(), visited.end(), false); if ((f = augmenting_path(G, s, t, path, visited)) > 0) { push_path(G, s, t, path, f); flow += f; } } while (f > 0); return flow; } int main(void) { int N, M; cin >> N >> M; Graph G(2*N+2); int s = 2*N, t = 2*N+1; const int INF = 1'000'000; // person i has nodes 2*i, 2*i+1 // // 2*i -> keep i on same team // 2*i+1 -> switch i to other team for (int i = 0; i < N; i++) { // can't do both G.add_edge(2*i, 2*i+1, INF); } for (int i = 0; i < M; i++) { int u, v; cin >> u >> v; u--; v--; // rivals must both switch or both stay put (or one of them removed) int u1 = 2*u, u2 = 2*u+1; int v1 = 2*v, v2 = 2*v+1; G.add_edge(u1, v2, INF); G.add_edge(v1, u2, INF); } string team; cin >> team; for (int i = 0; i < N; i++) { // discarded = 0 weight // team 1 = 1 weight // team 0 = 2 weight // // so real cost is 2-weight G.add_edge(s, 2*i, team[i] == '0' ? 2 : 1); G.add_edge(2*i+1, t, team[i] == '0' ? 1 : 2); } // find max weight independent set int flow = network_flow(G, s, t); int MWIS = 3*N - flow; cout << 2*N - MWIS << endl; return 0; }