基本概念:
点覆盖集:(vertex covering set,VCS)是无向图的一个点集,使得该图中所有边都至少有一个端点在该集合内。形象地说是若干个点“覆盖”住了与它们邻接的边,这些边恰好组成了原边集。
最小点权覆盖集:(minimum weight vertex covering set,MinWVCS)是在带点权无向图G中,点权之和最小的点覆盖集。
题意:对一个有n个点,m条边的有向图,有两种操作:
A(u)操作:删除点u的所有出边,即∀(u, v)∈E,操作花费代价w+(u);
B(u)操作:删除点u的所有入边,即∀(v, u)∈E,操作花费代价w-(u); 求(1 )删除原图所有边所需的最小话费 (2 )输出方案,如1 - , 2 +。。。
对于第一问,用最小割最大流解决。建图:增加源点s和汇点t ,,根据题意构造二分图,X和Y点集分别代表A(u)和 B(u)。从源点s向所有X集合点增加弧(s, u), 容量为w+(u);然后从左右Y集合中点出发向汇点t,增加弧(u, t),容量为w-(u); 若原图存在一条有向边(u, v), 则增加弧(u, v),容量为INF。答案便是s到t的最大流。
第二问:求完最大流后,对残量网络进行dfs,沿着所有未满流的弧走。对于所有(s, u),如果u被访问了,则未在u节点进行A操作。由于对于一条边必然有一个端点进行了操作,所以对于所有(u, t),如果u节点被访问了,则必有操作B(u)。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstdio> #include<queue> #include<vector> #include<cstring> using namespace std; const int maxn = 300; const int INF = 1e9; //Dinic模板 struct Edge { int from, to, cap, flow; }; int n, m, u, v, k, s, t; vector<int> G[maxn]; vector<Edge> edges; bool vis[maxn]; int d[maxn], cur[maxn]; inline void init() { for(int i=0; i<=t; i++) G[i].clear(); edges.clear(); } void add(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); k = edges.size(); G[from].push_back(k-2); G[to].push_back(k-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 max_flow() { int flow = 0; while(bfs()) { memset(cur, 0, sizeof(cur)); flow += dfs(s, INF); } return flow; } //Dinic模板 void dfs1(int u) //对残量网络进行dfs { vis[u] = 1; for(int i=0; i<G[u].size(); i++) { Edge v = edges[G[u][i]]; if(v.flow < v.cap &&!vis[v.to])//沿着未满流的弧走 dfs1(v.to); } } int main() { scanf("%d%d", &n, &m); //构图 s = 0; t = 2 * n + 1; init(); int val; for(int i=1; i<=n; i++) { scanf("%d", &val); add(i + n, t, val); } for(int i=1; i<=n; i++) { scanf("%d", &val); add(s, i, val); } while(m--) { scanf("%d%d", &u, &v); add(u, v + n, INF); } printf("%d\n", max_flow());//输出第一问 memset(vis, 0, sizeof(vis)); dfs1(s); int ans_cnt = 0; for(int i=1; i<=n; i++) { if(!vis[i]) ans_cnt++; if(vis[i + n]) ans_cnt++; } printf("%d\n", ans_cnt); //输出第二问 for(int i=1; i<=n; i++) { if(!vis[i]) printf("%d -\n", i); if(vis[i+n]) printf("%d +\n", i); } return 0; }