POJ 2987 Firing---dinic求最大流

正值点为S集,负值点为T集,源点s连接S集,T集连接汇点t;

建立二分图,求最大流,用正值的和减去最小割(最大流),即为所求

正值点代表开除,负值点代表 不被开除。如果有通路,则表示上司被开除,但下属没被开除,显然不成立,所以要求最小割,即最大流

画图感受一下吧!!

这题一些定义要用long long,不然会wa

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
long long n,ans,res,sum;
int ff[5050];
int d[5050];
struct node
{
	int b;
	long long len;
};
int flag;
vector<int> f[130000];
vector<node> edge;

bool bfs()  //bfs 分层
{
	memset(d,-1,sizeof(d));
	d[0]=0;
	queue<int> q;
	q.push(0);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		int l=f[u].size();
		for(int i=0;i<l;i++){
			if(edge[f[u][i]].len > 0 && d[edge[f[u][i]].b]<0){
				d[edge[f[u][i]].b]=d[u]+1;
				q.push(edge[f[u][i]].b);
			}
		}
	}
	if(d[n+1]>0)  return 1;
	else return 0;
}
long long dfs(int s,long long max)//dfs处理
{
	long long i,a;
	if(s==n+1 || max==0)  return max;
	int l=f[s].size();
	long long dt=max;
	for(i=0;i<l;i++){
		if(edge[f[s][i]].len > 0 && d[s]+1==d[edge[f[s][i]].b] &&( a=dfs(edge[f[s][i]].b,min(dt,edge[f[s][i]].len))) ){
			edge[f[s][i]].len -= a;
			edge[f[s][i]^1].len += a;
			dt-=a;
			if(dt==0) break;
		}
	}
	return max-dt;
}

void dfs1(int s) //找被开除的人数
{
	int l=f[s].size();
	for(int i=0;i<l;i++){
		if(edge[f[s][i]].len > 0 && ff[edge[f[s][i]].b]==0)
		{
			flag++;
			ff[edge[f[s][i]].b]=1;
			dfs1(edge[f[s][i]].b);
		}
	}
}
int main()
{
	int i,j,k;
	int m;
	node w;
	while(scanf("%d%d",&n,&m)!=EOF){
	sum=0;
	for(i=0;i<=n+1;i++)
		f[i].clear();
	edge.clear();
	for(i=1;i<=n;i++)
	{
		long long t;
		scanf("%lld",&t);
		if(t>=0){
			sum+=t;
			w.b=i;
			w.len=t;
			edge.push_back(w);//存正向边
			w.b=0;
			w.len=0;
			edge.push_back(w);//存反向边,反向边赋值为0
			int tt=edge.size();
			f[0].push_back(tt-2);
			f[i].push_back(tt-1);
		}
		else{
			w.b=n+1;
			w.len=-t;
			edge.push_back(w);
			w.b=i;
			w.len=0;
			edge.push_back(w);
			int tt=edge.size();
			f[i].push_back(tt-2);
			f[n+1].push_back(tt-1);
		}
	}
	for(i=1;i<=m;i++)
	{
		int a,c;
		scanf("%d%d",&a,&c);
		w.b=c;
		w.len=999999999999;
		edge.push_back(w);
		w.b=a;
		w.len=0;
		edge.push_back(w);
		int tt=edge.size();
		f[a].push_back(tt-2);
		f[c].push_back(tt-1);
	}
	ans=0,res=0;
	flag=0;
	while(bfs()){      //dinic算法
		while(res=dfs(0,999999999999)){
			ans += res;
		}
	}
	memset(ff,0,sizeof(ff));
	ff[0]=1;
	dfs1(0);//找被开除的人的个数,处理完的图,能走到的点,就是被开除人的个数
	printf("%d %lld\n",flag,sum-ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值