$[ USACO 2007 OPEN ] Dining$
阿新 • • 發佈:2018-10-01
lol 需要 space get 麻煩 ctype != 得到 clas
數組,如果出現了一側匹配一側不匹配的情況就將\(match\)數組還原。
\(\\\)
\(Description\)
有\(N\)頭牛,\(F\)種食物,\(D\)種飲料,每種食物和飲料只有一份。
現在已知每頭牛可以吃哪些食物,可以喝哪些飲料,問最多可以讓多少頭牛可以同時得到喜歡的食物和飲料。
- \(N,F,D\in [1,100]\)
\(\\\)
\(Solution@\)二分圖
這是一個最大匹配問題,但是需要兩側同時滿足可以增廣,有一側不合法就不計入答案。
直接兩側分別做一次匈牙利是有問題的。如果一側匹配上了,另一側沒有,那麽其實在\(DFS\)的過程中已經將某一側的匹配對象改變了,進而可能會導致下一步其他元素在匹配的時候匹配不上。
於是我們在每次增廣之前先備份一份\(match\)
\(\\\)
\(Code\)
出鍋了...\(Luogu\)上一道幾乎一樣的題交了就過了,但是這道題一直\(80\)。
下了一波數據發現好像是有的牛沒有喜歡的食物和飲料的鍋....
然後把這種情況算成合法又有一個點跪了...那個點裏好像這種情況又不算做合法了...
還是沒有想懂網絡流為啥過了...網絡流做似乎並沒有將這種特殊點算作合法...大爺們找到原因麻煩告訴我一聲...
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 110 #define R register #define gc getchar using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } int n,f,d,ans,tot1,hd1[N],tot2,hd2[N]; int m1[N],m2[N],vis1[N],vis2[N],tmp1[N],tmp2[N]; struct edge{int to,nxt;}e1[N*N],e2[N*N]; inline void add1(int u,int v){ e1[++tot1].to=v; e1[tot1].nxt=hd1[u]; hd1[u]=tot1; } inline void add2(int u,int v){ e2[++tot2].to=v; e2[tot2].nxt=hd2[u]; hd2[u]=tot2; } inline bool dfs1(int u,int t){ for(R int i=hd1[u],v;i;i=e1[i].nxt) if(vis1[v=e1[i].to]!=t){ vis1[v]=t; if(!m1[v]||dfs1(m1[v],t)){m1[v]=u;return 1;} } return 0; } inline bool dfs2(int u,int t){ for(R int i=hd2[u],v;i;i=e2[i].nxt) if(vis2[v=e2[i].to]!=t){ vis2[v]=t; if(!m2[v]||dfs2(m2[v],t)){m2[v]=u;return 1;} } return 0; } int main(){ n=rd(); f=rd(); d=rd(); for(R int i=1,x,y;i<=n;++i){ x=rd(); y=rd(); for(R int j=1,v;j<=x;++j){v=rd();add1(i,v);} for(R int j=1,v;j<=y;++j){v=rd();add2(i,v);} } for(R int i=1;i<=n;++i){ for(R int j=1;j<=f;++j) tmp1[j]=m1[j]; for(R int j=1;j<=d;++j) tmp2[j]=m2[j]; if(dfs1(i,i)&&dfs2(i,i)) ++ans; else{ for(R int j=1;j<=f;++j) m1[j]=tmp1[j]; for(R int j=1;j<=d;++j) m2[j]=tmp2[j]; } } printf("%d\n",ans); return 0; }
\(\\\)
\(Solution@\)網絡流
這是一個最大流問題,考慮有兩個限制同時滿足才可以將一個點視為合法,所以建圖考慮將限制分別放在牛的兩側,容量都為\(1\),這樣答案就轉化成了最大流。
有一種情況需要特殊考慮,如下圖,最大流是\(3\),實際上答案是\(1\),因為忽視了每頭牛的貢獻最多為\(1\)的限制。
於是有一個機智的做法,將每一個牛都拆成兩個點,連一條容量為\(1\)的邊,這樣每個牛最多只會允許一支流通過。
\(\\\)
\(Code\)
#include<cmath> #include<queue> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 10100 #define R register #define gc getchar #define inf 200000000 using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } int n,m1,m2,cnt,f[N],l[N],r[N],c[N]; int s,t,tot=1,hd[N],h[N],dp[N]; struct edge{int w,to,nxt;}e[N*100]; inline void add(int u,int v,int w){ e[++tot].to=v; e[tot].w=w; e[tot].nxt=hd[u]; hd[u]=tot; } queue<int> q; inline bool bfs(){ for(R int i=0;i<=cnt;++i) dp[i]=0; dp[s]=1; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(R int i=hd[u],v;i;i=e[i].nxt) if(e[i].w&&(!dp[v=e[i].to])){ dp[v]=dp[u]+1; q.push(v); } } return dp[t]>0; } inline int dfs(int u,int flow){ if(u==t||!flow) return flow; int res=0,tmp; for(R int &i=h[u];i;i=e[i].nxt) if(e[i].w&&(dp[e[i].to]==dp[u]+1)){ tmp=dfs(e[i].to,min(e[i].w,flow-res)); e[i].w-=tmp; e[i^1].w+=tmp; res+=tmp; if(res==flow) return res; } return res; } inline int dinic(){ int res=0; while(bfs()){ for(R int i=0;i<=cnt;++i) h[i]=hd[i]; res+=dfs(s,inf); } return res; } int main(){ n=rd(); m1=rd(); m2=rd(); for(R int i=1;i<=m1;++i) f[i]=++cnt; for(R int i=1;i<=n;++i) l[i]=++cnt,r[i]=++cnt; for(R int i=1;i<=m2;++i) c[i]=++cnt; s=0; t=++cnt; for(R int i=1;i<=m1;++i){add(s,f[i],1);add(f[i],s,0);} for(R int i=1;i<=n;++i){add(l[i],r[i],1);add(r[i],l[i],0);} for(R int i=1;i<=m2;++i){add(c[i],t,1);add(t,c[i],0);} for(R int i=1,a,b,x;i<=n;++i){ a=rd(); b=rd(); for(R int j=1;j<=a;++j){ x=rd(); add(f[x],l[i],1); add(l[i],f[x],0); } for(R int j=1;j<=b;++j){ x=rd(); add(r[i],c[x],1); add(c[x],r[i],0); } } printf("%d\n",dinic()); return 0; }
$[\ USACO\ 2007\ OPEN\ ]\ Dining$