P1231 教辅的组成 拆点限流
如果只有两个物品的话 是一个裸的二分图匹配问题
现在变成了三个物品之间的匹配 则只要在中间加一层节点表示书 再把这层的每个点拆成两个点中间连一条边限制流量 使其只能用一次
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<queue> using namespace std; #define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i) #define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i) #define mem(a, b) memset((a), b, sizeof(a)) int read() { int sum = 0, fg = 1; char c = getchar(); while(c < '0' || c > '9') { if (c == '-') fg = -1; c = getchar(); } while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * fg; } #define inf 0x3f3f3f3f const int maxn = 1000000; int e,be[maxn], ne[maxn], to[maxn], c[maxn]; int nb, nex, na,m1,m2; void add(int x, int y, int z) { to[e] = y, ne[e] = be[x], be[x] = e; c[e] = z, e++; to[e] = x, ne[e] = be[y], be[y] = e; c[e] = 0, e++; } int d[maxn], end; bool bfs() { queue<int>q; memset(d,-1,sizeof(d)); q.push(end),d[end] = 0; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = be[u]; i!=-1; i = ne[i]) { int v = to[i]; if(d[v] == -1 && c[i ^ 1]) { d[v] = d[u] + 1; q.push(v); } } } return d[0]!=-1; } int dfs(int x,int low) { if(x == end || !low)return low; int ret = 0; for(int i = be[x]; i!=-1;i = ne[i]) { int v = to[i]; if(d[v] == d[x] - 1 ) { int k = dfs(v,min(low-ret,c[i])); if(k > 0) { c[i] -= k; c[i^1] += k; ret+=k; } } } return ret; } int dinic() { int ans = 0; while(bfs()) { int k = dfs(0,inf); if(k>0)ans+=k; } return ans; } int main() { memset(be,-1,sizeof(be)); nb = read(); nex = read(); na = read(); m1 = read(); REP(i,1,m1) { int x,y; x = read(), y = read(); add(y,nex+x,1); } m2 = read(); REP(i,1,m2) { int x,y; x = read(), y = read(); add(nex+nb+x,2*nb+nex+y,1); } end = 2*nb+nex+na+1; REP(i,1,nex) add(0,i,1); REP(i,1,na) add(2*nb+nex+i,end,1); REP(i,1,nb) add(nex+i,nex+nb+i,1); printf("%d",dinic()); return 0; }