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;
}