题意: 有 M 个猪圈,每个猪圈里初始时有若干头猪。一开始所有猪圈都是关闭的。依次来了 N 个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪。每个顾客分别都有他能够买的数量的上限。每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。
分析: 此题是网络流,网络流里经典的构图题。将顾客看作除源和汇以外的节点,源和每个猪圈的第一个顾客连边,边的权是开始时猪圈中猪的数目,若源和某个节点之间有重边,则将权合并,每个猪圈的前后相邻两顾客之间连边,由前一个顾客指向后一个顾客。每个顾客和汇之间连边,边的权是顾客所希望购买的猪的数目。
网络流,需要对图进行化简
化简规则:
规律 1. 如果几个节点的流量的来源完全相同,则可以把它们合并成一个。
规律 2. 如果几个节点的流量的去向完全相同,则可以把它们合并成一个。
规律 3. 如果从点 u 到点 v 有一条容量为 +∞ 的边,并且 u 是 v 的唯一流量来源,或者 v 是 u 的唯一流量去向,则可以把 u 和 v 合并成一个节点。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
#define maxn 20000
#define INF 0x7fffffff
int n,m;
int en;
int st,ed; //源点和汇点
int dis[maxn] ;//dis[i],表示 到 原点 s 的 层数
int que[9999999];
int cur[maxn];
struct node
{
int pig,last;
};
node house[9999999];
int head[maxn];
int save[110][110];
int bfs()
{
memset(dis,-1,sizeof(dis));
dis[st]=0;
int h=0,tail=0;
tail++;
que[tail]=st;
while(h<tail)
{
h++;
int j=que[h];
for(int i=0;i<=ed;i++)
{
if(save[j][i]<=0) continue;
if(dis[i]==-1&&save[j][i])
{
dis[i] = dis[j]+ 1 ;
tail++;
que[tail]=i;
if(i==ed) return true;
}
}
}
return false;
}
int dfs(int x,int mx)
{
if(x==ed||mx==0) return mx;
int f,flow=0;
for(int i=0;i<=ed;i++)
{
if(save[x][i]<=0) continue;
if(dis[x]+1==dis[i]&&(f=dfs(i,min(mx,save[x][i]) )))
{
save[x][i]-=f;
save[i][x]+=f;
flow+=f;
mx-=f;
if(!mx)break;
}
}
return flow;
}
int dinic()
{
int tmp=0;
int maxflow=0;
while(bfs())
{
while(tmp=dfs(st,INF)) maxflow+=tmp;
}
return maxflow;
}
void init()
{
en=0;
st=0; //源
ed=n+1; //汇
memset(head,-1,sizeof(head));
memset(save,0,sizeof(save));
}
void build()
{
int key,open,cust;
for(int i=1;i<=m;i++)
{
scanf("%d",&house[i].pig);
house[i].last=0;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&key);
for(int j=0;j<key;j++)
{
scanf("%d",&open);
if(!house[open].last)
{
save[st][i]+=house[open].pig;
house[open].last=i;
}
else
{
save[house[open].last][i]=INF;
house[open].last=i;
}
}
scanf("%d",&cust);
save[i][ed]=cust;
}
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
init();
build();
printf("%d\n",dinic());
}
return 0;
}