POJ 2987 Firing 最大权闭合图
2013-06-18 16:02
375 查看
题目大意:你有N个员工,其中一部分不好好上班啦,所以你要开除他~~(好悲催...)但是不是所有员工都消极怠工,有些人还是会给公司赚点钱的嘛..你在开除一个员工的同时还必须连同他的下属一起开除..(由此可见跟一个好BOSS多重要..),由此你在开除人的时候就得斟酌一番了。每个员工都有自己的权值,代表开除他会给公司带来的收入(如果是负数的话就代表亏损),问开除哪些人会给公司带来的收益最大,还有在收益最大的时候开除的最少的人数。
解题思路:
1.求最大收益当然是标准的最大权闭合图的模型了。
对于每个员工的权值v[i] , 如果v[i]>0,加一条源点到i,容量为v[i]的边;
如果v[i]<0,加i到汇点,容量为-v[i]的边。
并定义个临时变量sum为所有v[i]为正的和。
对于每个上下级关系x y(y是x的下属), 加一条x->y 容量为INF的边,
求最小割, sum-最小割 的值就为最大的收益了。
2.求开除的最少的人数。
先说方法再证明。
求完最小割以后, 从源点开始, 按残留网络开始遍历 , 能遍历到的点数-1 (减掉源点) 就为最少开除的人数了。
首先证明为什么是能遍历到的点。(建议先看胡波涛的论文,接下来的证明基本上都是引用那篇论文的东西)
假设最小割把图分为源点所在点的集合N和汇点所在点的集合M,
由最小割的定义,从N到M是没有残余流量的,所以从源点遍历不能遍历到M,
而且求完最小割后N中的点即需要开除的员工
{见胡波涛论文 最大权=正权值和-最小割 那部分的证明,
假设N中正点权的权值和为X,负权值和为Y,则这题所求的最大收益为X-Y,
X和Y相关的点即为要开除的点}
接下来证明点数是唯一的,
点数不唯一的时候说明最小割不唯一,
这就说明在寻找增广路上有两个或多个最小值,
但我们在遍历的时候肯定找离源点最近的那个值,
就保证开除的点最少了。
由此得证
贴代码:
解题思路:
1.求最大收益当然是标准的最大权闭合图的模型了。
对于每个员工的权值v[i] , 如果v[i]>0,加一条源点到i,容量为v[i]的边;
如果v[i]<0,加i到汇点,容量为-v[i]的边。
并定义个临时变量sum为所有v[i]为正的和。
对于每个上下级关系x y(y是x的下属), 加一条x->y 容量为INF的边,
求最小割, sum-最小割 的值就为最大的收益了。
2.求开除的最少的人数。
先说方法再证明。
求完最小割以后, 从源点开始, 按残留网络开始遍历 , 能遍历到的点数-1 (减掉源点) 就为最少开除的人数了。
首先证明为什么是能遍历到的点。(建议先看胡波涛的论文,接下来的证明基本上都是引用那篇论文的东西)
假设最小割把图分为源点所在点的集合N和汇点所在点的集合M,
由最小割的定义,从N到M是没有残余流量的,所以从源点遍历不能遍历到M,
而且求完最小割后N中的点即需要开除的员工
{见胡波涛论文 最大权=正权值和-最小割 那部分的证明,
假设N中正点权的权值和为X,负权值和为Y,则这题所求的最大收益为X-Y,
X和Y相关的点即为要开除的点}
接下来证明点数是唯一的,
点数不唯一的时候说明最小割不唯一,
这就说明在寻找增广路上有两个或多个最小值,
但我们在遍历的时候肯定找离源点最近的那个值,
就保证开除的点最少了。
由此得证
贴代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 5010 #define M 200000 #define INF 1e9 struct { int to,next; int c; }edge[M]; int head ,level ,ip; int que ; bool makelevel(int s,int t) { memset(level,0,sizeof(level)); int iq=0,top; que[iq++]=s; level[s]=1; for(int i=0;i<iq;i++) { top=que[i]; if(top==t) return 1; for(int k=head[top];k!=-1;k=edge[k].next) { if(!level[edge[k].to]&&edge[k].c>0) { que[iq++]=edge[k].to; level[edge[k].to]=level[top]+1; } } } return 0; } long long dfs(int now,long long maxf,int t) { if(now==t) return maxf; long long ret=0,c; for(int k=head[now];k!=-1;k=edge[k].next) { if(edge[k].c>0&&level[edge[k].to]==(level[now]+1)) { c=dfs(edge[k].to,min(maxf-ret,(long long)edge[k].c),t); edge[k].c-=c; edge[k^1].c+=c; ret+=c; if(ret==maxf) return ret; } } if(!ret) level[now]-=2; return ret; } long long dinic(int s,int t) { long long ans=0; while(makelevel(s,t)) ans+=dfs(s,INF,t); return ans; } void add(int u,int v,int c,int f) //有向边f为0 ,否则为 c { edge[ip].to=v;edge[ip].c=c;edge[ip].next=head[u];head[u]=ip++; edge[ip].to=u;edge[ip].c=f;edge[ip].next=head[v];head[v]=ip++; } void dfs1(int pos,int &num) //从源点开始遍历 { level[pos]=1; num++; for(int p=head[pos];p!=-1;p=edge[p].next) if(!level[ edge[p].to ] && edge[p].c>0 ) dfs1(edge[p].to,num); } int main() { int n,m,x,y; while(cin>>n>>m) { memset(head,-1,sizeof(head)); ip=0; long long sum=0; for(int i=1;i<=n;i++) { scanf("%d",&x); if(x>0) { add(0,i,x,0); sum+=x; //求权值大于0的点权的和 } else if(x<0) add(i,n+1,-x,0); } while(m--) { scanf("%d%d",&x,&y); add(x,y,INF,0); } long long ans=sum-dinic(0,n+1); //最大收益=权值大于0的权值的和 - 最小割 memset(level,0,sizeof(level)); int num=-1; dfs1(0,num); //遍历从源点开始遍历到的点的数目 cout<<num<<' '<<ans<<endl; } return 0; }
相关文章推荐
- poj 2987 Firing 最大权闭合图
- poj 2987 Firing 最大权闭合图
- poj 2987 Firing【最大闭合子图】
- poj 2987 Firing 最大权闭合图
- poj 2987 Firing 最大权闭合图
- Firing (poj 2987 最大权闭合图)
- POJ 2987 Firing(最大流最小割の最大权闭合图)
- POJ 2987 Firing 最大权闭合子图
- poj 2987 Firing 最大权闭合子图
- POJ 2987 Firing (最大权闭合图,最小割)
- POJ 2987 Firing 最大权闭合子图
- POJ 2987 Firing 最大权闭合图
- POJ 2987 Firing【最大权闭合图-最小割】
- POJ_2987_Firing_(最大流+最大权闭合图)
- POJ 2987 Firing(最大权闭合)
- POJ 2987 Firing(最大权闭合)
- poj 2987 Firing 最大权闭合图 网络流
- 最大权闭合子图(poj 2987 Firing)
- POJ 2987 Firing 最小割(最大权闭合子图)
- poj - 2987 - Firing(最大权闭合图)