// poj 1149 PIGS (网络流) /* 网络流构图,用sap邻接表版过的,一开始用邻接阵,因为点有两千多个就MLE了,构图如下: 1. 源点s到客户连边,权值为客户所需求量 2. 客户到他所能打开的猪圈连边,权值为inf 3. 如果客户i能打开猪圈k,客户j(j<i) 也能打开猪圈k,则i连边到j(只连一次),权值为inf 4. 猪圈i拆点,i-i' 权值为猪圈中猪数量 5. 猪圈i' 到汇点 t 连边,权值为inf */ #include <iostream> #include <algorithm> using namespace std; const int null = -1; const int VMAX = 2100; const int EMAX = 300000; const int inf = 1<<26; struct Edge { int adj,next,re; //指向的点,下一边的下标,逆边的下标 int r; //余留网边的容量 }h[EMAX+10]; //用下标模拟指针构邻接表 int p[VMAX+10],c; int n,m,s,t; int gap[VMAX+10],pre[VMAX+10],dis[VMAX+10]; //插边,k,l为端点,cap为边容量 void insert(int k,int l,int cap) { h[++c].adj=l; h[c].r=cap; h[c].next=p[k]; p[k]=c; h[c].re=c+1; //逆边 h[++c].adj=k; h[c].r=0; h[c].next=p[l]; p[l]=c; h[c].re=c-1; //与上面对应 } int sap(int N) //N为点数(包括源汇) { memset(dis,0,sizeof(dis)); memset(gap,0,sizeof(gap)); gap[0]=N; int u=s,ca=inf,ans=0; while (dis[s]<N) { while (1) { bool flag=false; for (int j=p[u];j!=null;j=h[j].next) { int i=h[j].adj; if (h[j].r && dis[u]==dis[i]+1) { ca=min(ca,h[j].r); pre[i]=j;u=i; //注意邻接表版的pre表示的是边 if (i==t) { ans+=ca; while (i!=s) { u=pre[i]; h[u].r-=ca; h[h[u].re].r+=ca; i=h[h[u].re].adj; } u=s;ca=inf; } flag=true; break; } } if (!flag) break; } int Min=N; for (int j=p[u];j!=null;j=h[j].next) if (h[j].r && dis[h[j].adj]<Min) Min=dis[h[j].adj]; if (--gap[dis[u]] == 0) break; gap[dis[u]=Min+1]++; if (u!=s) u=h[h[pre[u]].re].adj; } return ans; } void init() { c=-1; memset(p,-1,sizeof(p)); } int main() { while (scanf("%d%d",&m,&n)!=EOF) { init(); int a,b,flag; s=0,t=n+2*m+1; for (int i=1;i<=m;i++) { scanf("%d",&a); insert(n+i,n+m+i,a); insert(n+m+i,t,inf); } for (int i=1;i<=n;i++) { scanf("%d",&a); while (a--){ scanf("%d",&b); insert(i,n+b,inf); for (int j=1;j<i;j++) { flag=0; for (int k=p[j];k!=null;k=h[k].next) { if (h[k].adj==i) {flag=0; break;} else if (h[k].adj==n+b) flag=1; } if (flag) insert(i,j,inf); } } scanf("%d",&b); insert(s,i,b); } printf("%d/n",sap(t+1)); } system("pause"); return 0; }