Time Limit: 2000MS | Memory Limit: 65536K | |||
Total Submissions: 7355 | Accepted: 2338 | Special Judge |
Description
Input
Output
Sample Input
3 6 1 2 3 4 2 1 1 2 1 1 3 2 1 2 3 1 2 3
Sample Output
5 3 1 + 2 - 2 +
Source
Northeastern Europe 2003, Northern Subregion
一道经典的最小割问题。每个点拆成两个点,分别代表入点和出点。原图中的边a-b,对应网络流建图中的aout-bin,容量为无穷大。然后每个点出边的代价,对应到原图中源点连向out点的容量,入边的代价,对应in点到汇点的代价。然后在这个图上求最小割。因为out点到in点的边容量为无穷大,所以它们肯定不会在最小割集中,而要使源点和汇点不连通,就只能删out和源点连接的边,或者in和汇点的连边。这刚好就对应了题目的要求。最后求割集的方案,不能直接根据满流的边得到割集(割集里的边一定是满流的,但满流的边不一定构成割集中的边)。而是从源点出发,沿着原图中不是满流的边遍历,能够到的点的集合设为S。再根据S中的点,判断割集。
#include<cstdio> #include<map> #include<queue> #include<cstring> #include<iostream> #include<algorithm> #include<vector> #include<list> #include<set> #include<cmath> using namespace std; const int maxn = 1e3 + 5; const int INF = 1e9; const double eps = 1e-6; typedef unsigned long long ULL; typedef long long LL; typedef pair<int, int> P; #define fi first #define se second struct Edge { int from, to, cap, flow; }; struct Dinic { int n, m, s, t; vector<Edge> edges; // 边数的两倍 vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 void ClearAll(int n) { for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void ClearFlow() { for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } void AddEdge(int from, int to, int cap) { //cout << from << ' ' << to << ' ' << cap << endl; edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if(x == t || a == 0) return a; int flow = 0, f; for(int& i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a == 0) break; } } return flow; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } void print(int n){ int use[maxn]; memset(use, 0, sizeof use); queue<int> q; while(!q.empty()) q.pop(); q.push(0); while(!q.empty()){ int pos = q.front(); q.pop(); for(int i = 0;i < G[pos].size();i++){ Edge& e = edges[G[pos][i]]; if(e.flow >= e.cap || e.cap==0 || use[e.to]) continue; use[e.to] = 1; q.push(e.to); } } vector<int> ans; ans.clear(); for(int i = 1;i <= 2*n;i++){ if(vis[i]==0 && i<=n){ ans.push_back(i); } else if(vis[i]==1 && i>n){ ans.push_back(i); } } cout << ans.size() << endl; for(int i = 0;i < ans.size();i++){ if(ans[i] > n) cout << ans[i]-n << " +" << endl; else cout << ans[i] << " -" << endl; } } }; Dinic g; int main(){ int n, m, cost; while(scanf("%d%d", &n, &m) != EOF){ g.ClearAll(2*n+5); int source = 0, sink = 2*n+1; for(int i = 1;i <= n;i++){ scanf("%d", &cost); g.AddEdge(i+n, sink, cost); } for(int i = 1;i <= n;i++){ scanf("%d", &cost); g.AddEdge(source, i, cost); } while(m--){ int x, y; scanf("%d%d", &x, &y); g.AddEdge(x, y+n, INF); } cout << g.Maxflow(source, sink) << endl; g.print(n); } return 0; }