Poj1149 PIGS (经典网络流)

顾客买猪问题


建图的方法:

当第i个猪圈第顾客K(不是每一个顾客都去打开每一个猪圈)一次被打开的时候, 就从超级源点连一条到顾客K的边, 权值为猪圈i里面猪的数量

当第i个猪圈不是是第一次被打开的时候, 那么就将上一个打开这个猪圈的人连到当前顾客, 权值为正无穷

将所有顾客都连一一条边倒超级汇点, 权值是顾客想买猪的数量


//SPA
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>

#define CLR(arr, val) memset(arr, val, sizeof(arr))
#pragma warning(disable:4996)

using namespace std;

const int MaxV = 1003;
const int MaxE = 10000 << 1;
const int INF = 1e9;

struct Graph
{
	struct Vertex
	{
		int head;
	}V[MaxV];

	struct Edge 
	{
		int v, c, f, next;
		Edge(){}
		Edge(int v,int c, int f, int next):v(v), c(c), f(f), next(next){}
	}E[MaxE];

	void init()
	{
		top = 0;
		CLR(V, -1);
	}
	void addEdge(int u, int v,int w)
	{
		E[top] = Edge(v, w, 0, V[u].head);
		V[u].head = top++;
		E[top] = Edge(u, 0, 0, V[v].head);
		V[v].head = top++;
	}
	int top;
};

int h[MaxV];        //高度
int path[MaxV];     //回路
int gap[MaxV];      //gap优化
int cur[MaxV];      //当前弧优化
int s, t;
int vexNum, edgeNum;
Graph g;

void setHeight()
{
	CLR(gap, 0);
	CLR(h, -1);
	h[t] = 0;

	queue<int>Q;
	Q.push(t);
	while(!Q.empty())
	{
		int top = Q.front();
		gap[ h[top] ]++;
		for(int i = g.V[top].head; i != -1; i = g.E[i].next)
		{
			int v = g.E[i].v;
			if(h[v] == -1)
			{
				h[v] = h[top] + 1;
				Q.push(v);
			}
		}
		Q.pop();
	}
}

int sap()
{
	setHeight();
	int maxFlow = 0, u = s;
	int flow = INF;
	for(int i = 0; i <= vexNum; i++)
		cur[i] = g.V[i].head;
	while(h[s] < vexNum)
	{
		int &i = cur[u];
		for(; i != -1; i = g.E[i].next)
		{
			int v = g.E[i].v;
			if(g.E[i].c > g.E[i].f && h[u] == h[v] + 1)
			{
				u = v;
				path[v] = i;
				flow = min(flow, g.E[i].c - g.E[i].f);
				if(u == t)
				{
					while(u != s)
					{
						int j = path[u];
						g.E[j].f += flow;
						g.E[j ^ 1].f -= flow;
						u = g.E[j ^ 1].v;
					}
					maxFlow += flow;
					flow = INF;
				}
				break;
			}
		}

		if(i == -1)
		{
			if(--gap[ h[u] ] == 0)
				break;
			int minH = vexNum - 1;
			cur[u] = g.V[u].head;
			for(int j = g.V[u].head; j != -1; j = g.E[j].next)
				if(g.E[j].c > g.E[j].f)
					minH = min(minH, h[g.E[j].v]);
			h[u] = minH + 1;
			++gap[ h[u] ];
			if(u != s)
				u = g.E[path[u] ^ 1].v;
		}
	}
	return maxFlow;	
}


int N, M;

int pagNum[1003]; //1000个猪圈, 猪圈里面猪的数量

int lastOpen[1003];   //1000个猪圈, lastopen[i] 是最后打开猪圈i的顾客


int main()
{
	while(scanf("%d %d", &M, &N) != EOF)
	{
		g.init();
		CLR(lastOpen, -1);
		s = 0, t = N + 1;
		vexNum = N + 2;
		for(int i = 1; i <= M; i++)
			scanf("%d", &pagNum[i]);
		for(int i = 1; i <= N; i++)
		{
			int A;
			scanf("%d", &A);
			while(A--)
			{
				int v;
				scanf("%d", &v);
				if(lastOpen[v] == -1) //第一次被打开
				{
					g.addEdge(s, i, pagNum[v]);
					lastOpen[v] = i;
				}
				else
				{
					g.addEdge(lastOpen[v], i, INF);
					lastOpen[v] = i;
				}
			}
			int B;
			scanf("%d", &B);
			g.addEdge(i, t, B);
		}
		printf("%d\n", sap());
	}
	return 0;
}

/*
2 2
2 2
2 1 2 1
1 2 10
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值