洛谷 P2891
阿新 • • 發佈:2018-11-25
這又是一道涉及到拆點的問題,有了上一題的經驗,這一題思路就打開了
我們這樣拆點
因為一頭牛隻能喝一種飲料,吃一種食物,而且一種食物,一種飲料 吃過了 就沒了
如果單純的構建
超級源點 --- 飲料 ----- 牛 ------ 食物 ------ 超級匯點
把源點到飲料 食物到匯點邊容量設為1,能保證不會大家分食物,
這種圖,很顯然 會出現一頭牛吃多個東西 ,就出錯了。
那麼 我們不如構造一個這樣的圖 :
超級源點 --- 飲料 ----- 牛入點 ------ 牛出點 ------ 食物 ------ 超級匯點
這樣拆了點之後,我們就達成了 限流這一思路 :
這也是網路流裡一個思想,無論你過來的有多大,我只要把某個關鍵邊的容量限制住,整個大流量就會被控制住,那麼我們把點拆分完之後,強制要求 牛入 ------ 牛出 之間容量只為 1 也就是說 你只能吃一個,不能夠多吃,這道問題也就順利解決了,不得不說網路流的靈活利用還是太重要了,尤其是拆點。
以下是 AC 程式碼
#include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; const int maxn = 1e6 + 5; const int INF = 0x3f3f3f3f; struct node { int v,w; int nxt; }ed[maxn]; int num; int head[maxn],h[maxn]; void adde(int u,int v,int w) { ed[++num].v = v; ed[num].nxt = head[u]; ed[num].w = w; head[u] = num; ed[++num].v = u; ed[num].nxt = head[v]; ed[num].w = 0; head[v] = num; } int st,en; bool bfs() { memset(h,0,sizeof(h)); h[st]=1; queue<int> q; q.push(st); while(q.size()) { int u = q.front(); q.pop(); for(int i=head[u];i;i=ed[i].nxt) { int v = ed[i].v; if(ed[i].w && h[v] == 0) { h[v] = h[u] + 1; q.push(v); } } } if(h[en] == 0) return false; else return true; } int dfs(int u,int val) { int rec; if(u == en) return val; for(int i=head[u];i;i=ed[i].nxt) { int v = ed[i].v; if(h[v] == h[u] + 1 && ed[i].w && (rec = dfs(v, min(val, ed[i].w)))) { ed[i].w -= rec; ed[i ^ 1].w += rec; return rec; } } return 0; } int dinic() { int ans = 0; int minf; while(bfs()) while((minf = dfs(1, INF))) ans += minf; return ans; } int n,f,d; int main() { scanf("%d%d%d",&n,&f,&d); num=1; st=1; en=1+f+n+d+1; for(int i=1;i<=f;i++) { adde(st,1+i,1); } for(int i=1;i<=d;i++) { adde(1+f+n+i,en,1); } for(int i=1;i<=n;i++) { adde(1+f+i,1+f+n+d+1+i,1); } for(int i=1;i<=n;i++) { int dn,fn; scanf("%d%d",&fn,&dn); for(int q=1;q<=fn;q++) { int fi; scanf("%d",&fi); adde(1+fi,1+f+i,1); } for(int q=1;q<=dn;q++) { int di; scanf("%d",&di); adde(1+f+n+d+1+i,1+f+n+di,1); } } printf("%d\n",dinic()); return 0; }