解题思路
应该比较显然得能看出这是个网络流,将$S$与房间连边,房间与人连边,人与菜连边,菜与汇点连边,边的流量均为1。但这样是错误的,因为有可能一个人跑过去2的流量,所以要将人拆点限流。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<queue> using namespace std; const int MAXN = 505; const int MAXM = 50005; const int inf = 0x3f3f3f3f; inline int rd(){ int x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x; } int n,p,q,head[MAXN],cnt=1,to[MAXM<<1],nxt[MAXM<<1],val[MAXM<<1]; int ans,S,T=500,d[MAXN]; queue<int> Q; inline void add(int bg,int ed,int w){ to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,head[bg]=cnt; } inline bool bfs(){ while(Q.size()) Q.pop(); memset(d,0,sizeof(d));d[S]=1;Q.push(S); while(Q.size()){ int x=Q.front();Q.pop(); for(register int i=head[x];i;i=nxt[i]){ int u=to[i]; if(!d[u] && val[i]){ d[u]=d[x]+1; Q.push(u); if(u==T) return true; } } } return false; } int dinic(int x,int flow){ if(x==T) return flow; int res=flow,k; for(register int i=head[x];i && res;i=nxt[i]){ int u=to[i]; if(d[u]==d[x]+1 && val[i]){ k=dinic(u,min(val[i],flow)); if(!k) d[u]=0; val[i]-=k;val[i^1]+=k;res-=k; } } return flow-res; } int main(){ n=rd(),p=rd(),q=rd();int x; for(int i=1;i<=p;i++) add(S,i,1),add(i,S,0); for(int i=1;i<=n;i++) for(int j=1;j<=p;j++){ x=rd();if(!x) continue; add(j,i+p,1),add(i+p,j,0); } for(int i=1;i<=n;i++) add(i+p,i+p+n,1),add(i+p+n,i+p,0); for(int i=1;i<=n;i++) for(int j=1;j<=q;j++){ x=rd();if(!x) continue; add(i+p+n,j+n*2+p,1),add(j+n*2+p,i+p+n,0); } for(int i=1;i<=q;i++) add(i+n*2+p,T,1),add(T,i+n*2+p,0); while(bfs()) ans+=dinic(S,inf); cout<<ans; return 0; }