顾客买猪问题
建图的方法:
当第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
*/