题意:
公司要进行裁员,裁掉一个人可以获得一定的利益或损失(用正数和负数表示)。
一些人之间有附属关系,比如b是a的下属,则裁掉a就必须裁掉b。
问要取得最大的利益,最少要裁掉几个人,取得的最大利益是多少
结论一:最大权闭合图的的权=原图中权值为正的点的和 - 最小割(最大流)
结论二:最小割对应取点方案就是最小取点数
具体详细证明过程 请参考:胡伯涛的论文《最小割模型在信息学竞赛中的应用》链接
大佬的证明:
结论一:
首先引入结论,最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图,接下来我们来说明一些结论。
证明:最小割为简单割。
引入一下简单割的概念:割集的每条边都与S或T关联。(请下面阅读时一定分清最小割与简单割,容易混淆)
那么为什么最小割是简单割呢?因为除S和T之外的点间的边的容量是正无穷,最小割的容量不可能为正无穷。所以,得证。
证明网络中的简单割与原图中闭合图存在一一对应的关系。(即所有闭合图都是简单割,简单割也必定是一个闭合图)。
证明闭合图是简单割:如果闭合图不是简单割(反证法)。那么说明有一条边是容量为正无穷的边,则说明闭合图中有一条出边的终点不在闭合图中,矛盾。
证明简单割是闭合图:因为简单割不含正无穷的边,所以不含有连向另一个集合(除T)的点,所以其出边的终点都在简单割中,满足闭合图定义。得正。
证明最小割所产生的两个集合中,其源点S所在集合(除去S)为最大权闭合图。
首先我们记一个简单割的容量为C,且S所在集合为N,T所在集合为M。
则C=M中所有权值为正的点的权值(即S与M中点相连的边的容量)+N中所有权值为负的点权值的绝对值(即N中点与T中点相连边的容量)。记(C=x1+y1);(很好理解,不理解画一个图或想象一下就明白了)。
我们记N这个闭合图的权值和为W。
则W=N中权值为正的点的权值-N中权值为负的点的权值的绝对值。记(W=x2-y2);
则W+C=x1+y1+x2-y2。
因为明显y1=y2,所以W+C=x1+x2;
x1为M中所有权值为正的点的权值,x2为N中权值为正的点的权值。
所以x1+x2=所有权值为正的点的权值之和(记为TOT).
所以我们得到W+C=TOT.整理一下W=TOT-C.
到这里我们就得到了闭合图的权值与简单割的容量的关系。
因为TOT为定值,所以我们欲使W最大,即C最小,即此时这个简单割为最小割,此时闭合图为其源点S所在集合(除去S)。得正。
至此,我们就将最大权闭合图问题转化为了求最小割的问题。求最小割用最小割容量=最大流,即可将问题转化为求最大流的问题。
结论二:
下面证明最小割对应取点方案就是最小取点数
由于原图是个DAG图,所以对于取得的最大权闭合图K,取它的任意一个子图G,如果从K-G仍然是一个闭合图,那么的点权和一定大于等于0,例如:1->2,2->3,1->4,4->5,若最大权闭合图为:{1,2,3,4,5},那么其中任一满足条件的G({1},{1,2},{1,2,3},{1,4},{1,4,5})点权和一定大于等于0,否则去除G,K-G仍然为闭合图,但是K-G的点权和会大于K
所以如果有两种取点方式使得权值相同,但是取点数不同的话,那么肯定存在一个可以移除的满足条件的子图G,其点权和为0
下面考虑构造的网络,对于G在网络中的对应图G',由于在网络求的是最小割,即最大流,而且G的点权和为0,所以G'中与源点S连边的容量和等于G'中与汇点T连边的流量和,同时由于去除G后K还是一个闭合图,所以只有可能G'中的流量流入K'-G',不可能有流量从K'-G'流入G',所以G'的边中除了流量为inf的那些,一定是满流的
再考虑在残留网络中求出取点集的方法,从源点开始floodfill,忽略满流边,即残留网络中的0流边,可以遍历到的点就是要取的点集了,这个道理想一下简单割和闭合图的取法一一对应就可以了
那么G'既然是满流的,在残留网络中就不可能对这些0流边进行处理,那就不会取到G中的点进入取点集,所以建立网络求得得最小割对应的取法取出的就是最小的点数了
(求点方法二:
再回到本题,第二问求最大利益,显然就是最大权闭合图。
第一问问最少裁掉几个人,我采用的是边权放大的方法:
所有上述3中的边扩大一个很大的倍数k,再减去1,所有上述4中的边扩大同样k倍,再加上1。
设num为一共减去了多少个1,则(maxflow + num) / k仍是最大权闭合图的权,而(maxFlow + num) % k就是第一问的答案。
)
代码
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const long long INF=1e16;
const int INFF=0x3f3f3f3f;
struct edge
{
int to,rev;
long long cap;
edge(int x,long long y,int z)
{
to=x;cap=y;rev=z;
}
};
vector<edge> g[5111];
int level[5555],iter[5555],n,m,s;
long long sum;
bool vis[5555];
void add(int from,int to,int cap)
{
g[from].push_back(edge(to,cap,g[to].size()));
g[to].push_back(edge(from,0,g[from].size()-1));
}
void bfs(int s)
{
memset(level,-1,sizeof(level));
level[s]=0;
queue<int> que;
que.push(s);
while(!que.empty())
{
int v=que.front();que.pop();
for(int i=0;i<g[v].size();i++)
{
edge &e=g[v][i];
if(e.cap>0&&level[e.to]<0)
{
level[e.to]=level[v]+1;
que.push(e.to);
}
}
}
}
long long dfs(int v,int t,long long f)
{
if(v==t)return f;
for(int &i=iter[v];i<g[v].size();i++)
{
edge &e=g[v][i];
if(e.cap>0&&level[e.to]>level[v])
{
long long d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
g[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
long long max_flow(int s,int t)
{
long long flow=0;
while(1)
{
bfs(s);
if(level[t]<0)return flow;
memset(iter,0,sizeof(iter));
long long f;
while((f=dfs(s,t,INF))>0)
{
flow+=f;
}
}
}
int dfs1(int u)
{
vis[u]=true;
s++;
for(int v=0;v<g[u].size();v++)
{
edge &e=g[u][v];
if(e.cap>0&&!vis[e.to])
{
vis[e.to]=true;
dfs1(e.to);
}
}
}
int main()
{
int x,y;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<=n+1;i++)
g[i].clear();
sum=0;
long long xx;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
xx=xx*10000;
if(x>0)
{
add(0,i,x);
sum+=x;
}
else
add(i,n+1,-x);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y,INF);
}
long long ans=sum-max_flow(0,n+1);
s=-1;
memset(vis,false,sizeof(vis));
dfs1(0);
printf("%d %I64d\n",s,ans);
}
return 0;
}