poj 1149 PIGS

http://poj.org/problem?id=1149

纠结了很久才会做的最大流。

按照朴素的思想我们可以建立如下的图:

假设在某一轮交易中i号猪圈被打开,那么在下一次有人可以打开i号猪圈的时候,我们只需将这两个人之间建立一条流量为无穷的边即可。还有一点是, 在一条路径S到达人的路径中,流量为无穷的边可以直接删除,因为相当于不需要一个中转站,两个节点可以直接相连。这样的话我们只需要保留S(0),猪圈(1, m),人(m+1, m+n),T(m+n+1)这些点来建图。经过此番简化就可以建图辣!

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;

const int INF = 1e9;

int Q[2500],h,t;
int level[2500],S,T;
int head[2500], cnt;
int c[2500],pre[2500];

struct edge{
	int v,c,next;
}node[250000];

void Init(){
	cnt = 0;
	memset(head, -1, sizeof(head));
}

void addedge(int u, int v, int c){
	//cout << u << " " << v << " " << c << endl;
	node[cnt].v = v;
	node[cnt].c = c;
	node[cnt].next = head[u];
	head[u] = cnt++;
}

bool BFS(){//构建层次图
	memset(level, -1, sizeof(level));
	level[S] = 0, h = 0, t = 1, Q[1] = S;
	while(h < t){
		int u = Q[++h];
		for(int i=head[u]; i!=-1; i=node[i].next){
			int v = node[i].v;
			if(node[i].c > 0 && level[v] == -1){
				level[v] = level[u] + 1;
				Q[++t] = v;
				if(v == T) return true;
			}
		}
	}
	return false;
}

int DINIC(int id, int minflow){//返回增广路的瓶颈流量
	if(id == T) return minflow;
	int a = 0;
	for(int i=head[id]; i!=-1; i=node[i].next){
		int v = node[i].v;
		if(node[i].c > 0 && level[v] == level[id] + 1
			&& (a = DINIC(v, min(minflow, node[i].c)))){
			node[i].c -= a;
			node[i^1].c += a;
			return a;
		}
	}
	return 0;
}

int main(){
	int m,n;
	while(scanf("%d%d",&m,&n) == 2){
		Init();
        S = 0, T = m+n+1;
		for(int i=1; i<=m; i++){//源点到猪圈的边
			scanf("%d",&c[i]);
			addedge(S, i, c[i]);
			addedge(i, S, 0);
		}
		for(int i=1; i<=m; i++) pre[i] = i;
		for(int i=1; i<=n; i++){
			int a,b;
			scanf("%d",&a);
			for(int j=1; j<=a; j++){//将猪圈与猪圈间的边转化为人与人之间的边
				int u;
				scanf("%d",&u);
				addedge(pre[u], m+i, INF);
				addedge(m+i, pre[u], 0);
				pre[u] = m+i;
			}
			scanf("%d",&b);
			addedge(m+i, m+n+1, b);//人与汇点之间的边
			addedge(m+n+1, m+i, 0);
		}
		int tmp, ans = 0;
		while(BFS()){
			while(tmp = DINIC(S, INF)) ans += tmp;
		}
		printf("%d\n",ans);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值