传送门
解析:
今天
D
Z
Y
O
DZYO
DZYO讲的最难的一道题,课上怎么都想不出来的坑题。。。
建图真是太巧妙了。
思路:
首先对于猪圈中初始猪的数量,这很好解决,直接从源点连一条容量为初始数量的边就可以了。
首先想到的是将每一次顾客要买猪的时候建一个虚点,向汇点连边,容量为买猪的数量,然后开放的猪圈各自相邻的连边,容量为 I N F INF INF,然后每个顾客会买的猪圈就向后新建立虚点,连容量为 I N F INF INF,然而这样做边和点实在是太多了。。。
然后想到了合并点,每次要买哪些猪圈就直接将哪些猪圈合并成一个点。
但是这样有着显然的错误,比如,
1
1
1号客人买了
1
1
1号和
2
2
2号猪圈的猪,我们将
1
1
1,
2
2
2号猪圈合并。
2
2
2号客人买了
2
2
2号和
3
3
3号猪圈的猪,我们将
1
1
1,
2
2
2,
3
3
3号猪圈合并。
那么当
3
3
3号客人要买
1
1
1号猪圈的猪的时候,显然他无论如何都不可能买到原来呆在
3
3
3号猪圈的猪,但是我们已经将它们合并了!这里就出现了问题。
其实上面的方法略微改进就是正确解法,
我们对每次顾客的购买建立虚点,向汇点连容量为购买量的边,将所有猪圈连过来,并记录这些猪圈被最后一个顾客打开的编号为 L a s t Last Last,然后下一次有顾客要购买这里的猪圈的猪的时候,我们从之前记录的 L a s t Last Last编号的顾客的虚结点连容量为 I N F INF INF的边。
这样跑最大流就能够得到合法情况下的最优答案。
代码:
#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=1103,M=1000000,INF=0x3f3f3f3f;
cs int S=0,T=1101;
int last[N],nxt[M<<1],to[M<<1],ecnt=1;
int cap[M<<1];
inline void addedge(int u,int v,int val){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}
int lev[N],cur[N];
inline bool BFS(){
memset(lev,-1,sizeof lev);
queue<int> q;
q.push(S);cur[S]=last[S];
lev[S]=0;
while(!q.empty()){
int u=q.front();
q.pop();
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]==-1){
lev[v]=lev[u]+1;
if(v==T)return true;
cur[v]=last[v];
q.push(v);
}
}
}
return false;
}
inline int Dinic(cs int &u,cs int &flow){
if(u==T)return flow;
int ans=0;
for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]>lev[u]){
int delta=Dinic(v,min(flow-ans,cap[e]));
if(delta){
cap[e]-=delta;
cap[e^1]+=delta;
ans+=delta;
if(ans==flow)return ans;
}
}
}
return ans;
}
inline int maxflow(){
int ans=0;
while(BFS())ans+=Dinic(S,INF);
return ans;
}
int Last[N];
int n,m;
signed main(){
m=getint(),n=getint();
for(int re i=1;i<=m;++i){
int val=getint();
Last[i]=i;
addedge(S,i,val);
}
for(int re i=1;i<=n;++i){
int A=getint();
while(A--){
int p=getint();
if(Last[p]!=i+1000)
addedge(Last[p],i+1000,INF);
Last[p]=i+1000;
}
int B=getint();
addedge(i+1000,T,B);
}
cout<<maxflow();
return 0;
}