/** 题目:poj2987 Firing 最大权闭合子图 边权有正有负 链接:http://poj.org/problem?id=2987 题意:由于金融危机,公司要裁员,如果裁了员工x,那么x的下级都要裁掉,如果x的下级被裁掉,那么x的下级的下级也要裁掉。。。依次类推 每个员工有个价值,公司裁了员工i,获得价值wi(有正有负), 问公司如何裁员获得最大价值。输出裁员人数以及最大价值。 思路:最大权闭合子图。如果员工x价值wx为正,s->x,cap=wx; 如果员工y价值为wx为负,x->t,cap=-wx; 如果员工x是y的上级,x->y,cap=INF; 求s-t最大流,结果为所有正价值之和-最大流。 或者: 求s-t最大流之后,此时dinic的vis为1的点表示在该最小割的S集合内。S集合除去s点的其他点就是需要裁员的人,从中可以获取人数,以及计算价值和。
新增附加源s和附加汇t,从s向所有的正权点引一条边,容量为权值;从所有的负权点向汇点引一条边,容量为负权的相反数。求出最小割以后,S-{s}就是最大闭合子图。 */ #include<iostream> #include<cstring> #include<vector> #include<map> #include<cstdio> #include<sstream> #include<algorithm> #include<queue> using namespace std; typedef long long LL; const int INF = 0x3f3f3f3f; const int N = 5005; struct Edge{ int from, to, cap, flow; Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){} }; struct Dinic{ int n, m, s, t; vector<Edge> edges; vector<int> G[N]; bool vis[N]; LL d[N]; int cur[N]; void init(int n) { this->n = n; for(int i = 0; i <= n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap) { 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); d[s] = 0; vis[s] = 1; 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]; } LL DFS(int x,LL a){ if(x==t||a==0) return a; LL 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,(LL)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; } LL Maxflow(int s,int t){ this->s = s, this->t = t; LL flow = 0; while(BFS()){ memset(cur, 0, sizeof cur); flow += DFS(s,INF); } return flow; } }; int main() { int n, m; while(scanf("%d%d",&n,&m)==2) { int s = 0, t = n+1; Dinic dinic; dinic.init(t); int w; LL sum = 0; for(int i = 1; i <= n; i++){ scanf("%d",&w); if(w>=0){ sum += w; dinic.AddEdge(s,i,w); }else dinic.AddEdge(i,t,-w); } int u, v; for(int i = 1; i <= m; i++){ scanf("%d%d",&u,&v); dinic.AddEdge(u,v,INF); } LL flow = dinic.Maxflow(s,t); int num = 0; for(int i = 1; i <= n; i++){ if(dinic.vis[i]){ num++; } } printf("%d %lld ",num,sum-flow); } return 0; }