基本概念:
点覆盖集:(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;
}