1. 程式人生 > >$[ USACO 2007 OPEN ] Dining$

$[ USACO 2007 OPEN ] Dining$

lol 需要 space get 麻煩 ctype != 得到 clas


\(\\\)

\(Description\)


\(N\)頭牛,\(F\)種食物,\(D\)種飲料,每種食物和飲料只有一份。

現在已知每頭牛可以吃哪些食物,可以喝哪些飲料,問最多可以讓多少頭牛可以同時得到喜歡的食物和飲料。

  • \(N,F,D\in [1,100]\)

\(\\\)

\(Solution@\)二分圖


這是一個最大匹配問題,但是需要兩側同時滿足可以增廣,有一側不合法就不計入答案。

直接兩側分別做一次匈牙利是有問題的。如果一側匹配上了,另一側沒有,那麽其實在\(DFS\)的過程中已經將某一側的匹配對象改變了,進而可能會導致下一步其他元素在匹配的時候匹配不上。

於是我們在每次增廣之前先備份一份\(match\)

數組,如果出現了一側匹配一側不匹配的情況就將\(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$