POJ 2987 Firing(最大权闭合子图)
2016-08-10 10:46
453 查看
Description
一个公司有n名员工,员工之间有一些从属关系,裁掉每位员工会带来一个价值(可正可负),现在公司要裁掉一些员工,规定每裁掉一个员工就要裁这个员工的所有下属,问如何裁员能使得剩余员工的总价值最大,此时需要裁掉多少名员工
Input
第一行两个整数n和m表示员工个数和从属关系数,之后n个整数wi表示裁掉每位员工的价值,最后m行每行两个整数a和b表示a是b的上司
(0<=n<=5000,0<=m<=60000,|wi|<=10^7)
Output
输出两个整数,第一个整数表示要裁掉的员工人数,第二个整数表示剩余员工总价值
Sample Input
5 5
8
-9
-20
12
-10
1 2
2 5
1 4
3 4
4 5
Sample Output
2 2
Solution
所谓裁掉某人就要裁掉其下属其实就是选择u就必须选择u的后继,问题转化为从关系图中找一个最大权闭合子图,最大权闭合子图的求法如下:
增设源汇点
对于具有正点权wi的点i,从源点向i建容量为wi的边
对于具有负点权wi的点i,从i向汇点建容量为-wi的边
对于关系i->j,从i到j建容量为INF的边
跑一边最大流,那么最大权闭合子图权值和=正权值之和-最小割(最大流)
此处最大权闭合子图的权值和就是裁员带来的最大价值,裁掉的员工人数就是以割为界,靠近源点一边的点数
Code
一个公司有n名员工,员工之间有一些从属关系,裁掉每位员工会带来一个价值(可正可负),现在公司要裁掉一些员工,规定每裁掉一个员工就要裁这个员工的所有下属,问如何裁员能使得剩余员工的总价值最大,此时需要裁掉多少名员工
Input
第一行两个整数n和m表示员工个数和从属关系数,之后n个整数wi表示裁掉每位员工的价值,最后m行每行两个整数a和b表示a是b的上司
(0<=n<=5000,0<=m<=60000,|wi|<=10^7)
Output
输出两个整数,第一个整数表示要裁掉的员工人数,第二个整数表示剩余员工总价值
Sample Input
5 5
8
-9
-20
12
-10
1 2
2 5
1 4
3 4
4 5
Sample Output
2 2
Solution
所谓裁掉某人就要裁掉其下属其实就是选择u就必须选择u的后继,问题转化为从关系图中找一个最大权闭合子图,最大权闭合子图的求法如下:
增设源汇点
对于具有正点权wi的点i,从源点向i建容量为wi的边
对于具有负点权wi的点i,从i向汇点建容量为-wi的边
对于关系i->j,从i到j建容量为INF的边
跑一边最大流,那么最大权闭合子图权值和=正权值之和-最小割(最大流)
此处最大权闭合子图的权值和就是裁员带来的最大价值,裁掉的员工人数就是以割为界,靠近源点一边的点数
Code
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<queue> using namespace std; typedef long long ll; #define maxn 5555 #define maxm 222222 #define INF 0x3f3f3f3f int head[maxn],cur[maxn],d[maxn],st[maxm],s,e,no;//s为源点,e为汇点,n为点数,no为边数 struct point { int u,v,flow,next; point(){}; point(int x,int y,int z,int w):u(x),v(y),next(z),flow(w){}; }p[maxm]; void add(int x,int y,int z)//从x到y建容量为z的边 { p[no]=point(x,y,head[x],z);//前向弧,标号为偶 head[x]=no++; p[no]=point(y,x,head[y],0);//后向弧,标号为奇 head[y]=no++; } void init()//初始化 { memset(head,-1,sizeof(head)); no=0; } bool bfs() { int i,x,y; queue<int>q; memset(d,-1,sizeof(d)); d[s]=0; q.push(s); while(!q.empty()) { x=q.front(); q.pop(); for(i=head[x];i!=-1;i=p[i].next) { if(p[i].flow&& d[y=p[i].v]<0) { d[y]=d[x]+1; if(y==e) return true; q.push(y); } } } return false; } ll dinic()//最大流 { int i,loc,top,x=s; ll nowflow,maxflow=0; while(bfs()) { memcpy(cur,head,sizeof(head)); top=0; while(true) { if(x==e) { nowflow=INF; for(i=0;i<top;i++) { if(nowflow>p[st[i]].flow) { nowflow=p[st[i]].flow; loc=i; } } for(i=0;i<top;i++) { p[st[i]].flow-=nowflow; p[st[i]^1].flow+=nowflow; } maxflow+=nowflow; top=loc; x=p[st[top]].u; } for(i=cur[x];i!=-1;i=p[i].next) if(p[i].flow&&d[p[i].v]==d[x]+1) break; cur[x]=i; if(i!=-1) { st[top++]=i; x=p[i].v; } else { if(!top) break; d[x]=-1; x=p[st[--top]].u; } } } return maxflow; } int n,m,cnt,vis[maxn]; void dfs(int u) { cnt++; vis[u]=1; for(int i=head[u];~i;i=p[i].next) { int v=p[i].v; if(p[i].flow>0&&!vis[v])dfs(v); } } int main() { while(~scanf("%d%d",&n,&m)) { init(); s=0,e=n+1; ll sum=0; for(int i=1;i<=n;i++) { int w; scanf("%d",&w); if(w>0)sum+=w,add(s,i,w); else add(i,e,-w); } while(m--) { int u,v; scanf("%d%d",&u,&v); add(u,v,INF); } ll ans=dinic(); memset(vis,0,sizeof(vis)); dfs(s); printf("%d %lld\n",cnt-1,sum-ans); } return 0; }
相关文章推荐
- poj 2987 Firing 最大权闭合子图
- poj 2987 Firing【最大权闭合子图+玄学计数 || BFS】
- POJ 2987 Firing 最大权闭合子图
- poj 2987 Firing 最大权闭合子图
- POJ 2987 - Firing 比较完善的最大权闭合子图..维护两个最优值...
- poj 2987 Firing (最大权闭合子图)
- POJ 2987 Firing 最大权闭合子图
- poj 2987 Firing【最大闭合子图】
- POJ 2987 Firing (最大权闭合子图Dinic)
- 【POJ】2987 Firing 最大权闭合子图
- POJ 2987 Firing 最小割(最大权闭合子图)
- 【POJ 2987】Firing (最小割-最大权闭合子图)
- 最大权闭合子图(poj 2987 Firing)
- poj 2987 Firing 最大权闭合子图
- poj 2987 最大闭合子图
- poj - 2987 - Firing(最大权闭合图)
- POJ_2987_Firing(最大权闭合图)
- poj - 2987 - Firing(最大权闭合图)
- POJ 2987 Firing 最大权闭合图
- POJ 2987 Firing 最大权闭合图