P2891 [USACO07OPEN] Dining G
Sol
OK,看到题面,$s \to $ 食物 $ \to $ 奶牛 $ \to $ 饮料 $ \to t$,流量全 \(1\),太水了。
然后 \(20 pts\)。
显然这个题的前面没有标“【模板】”,是不会很简单的。
为什么会错,举一个例子:
s -> Food1
s -> Food2
Food1 -> Cow
Food2 -> Cow
Cow -> Drink1
Cow -> Drink2
Drink1 -> t
Drink2 -> t
然后我们的程序成功的输出了 \(2\),是错的,因为我们没有限制每头奶牛只能有一组食物与饮料。
于是我们需要用到最大流的另一种套路——拆点,我们把奶牛拆成奶牛入点和奶牛出点,流量设 \(1\),就保证了每头奶牛最多有一组食物与饮料。
最终的建模:$s \to $ 食物 $ \to $ 奶牛入点 $ \to $ 奶牛出点 $ \to $ 饮料 $ \to t$,感觉还是好水。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x(0);
short f(1);
char ch(getchar());
while(!isdigit(ch))f=(ch=='-')?-1:1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
const int N=2005;
class eg
{
public:
int to,w;
int unsigned ip;
};
int n,F,D,s,t,dep[N],hig[N];
vector<eg>nbr[N];
bool bfs()
{
queue<int>q;
q.push(s);
for(int i=s;i<=t;i++)
dep[i]=-1,hig[i]=0;
dep[s]=0;
while(q.empty()==false)
{
int cur=q.front();
q.pop();
for(auto dat:nbr[cur])
{
int nxt=dat.to,w=dat.w;
if(w>0&&dep[nxt]==-1)
{
dep[nxt]=dep[cur]+1;
q.push(nxt);
}
}
}
return dep[t]>=0;
}
int dfs(int cur,int now)
{
if(cur==t)
return now;
int num=0;
for(int i=hig[cur];i<nbr[cur].size();i=hig[cur])
{
hig[cur]=i+1;
int nxt=nbr[cur][i].to,w=nbr[cur][i].w,ip=nbr[cur][i].ip;
if(w>0&&dep[nxt]==dep[cur]+1)
{
int h=dfs(nxt,min(now,w));
nbr[cur][i].w-=h;
nbr[nxt][ip].w+=h;
now-=h;
num+=h;
if(now==0)
return num;
}
}
return num;
}
void add(int x,int y,int w=1)
{
nbr[x].push_back({y,w,nbr[y].size()});
nbr[y].push_back({x,0,nbr[x].size()-1ull});
return ;
}
signed main()
{
// freopen("in.in","r",stdin
// freopen("out,out","w",stdout);
n=read(),F=read(),D=read();
s=0,t=F+n+n+D+1;
for(int i=1;i<=F;i++)
add(s,i);
for(int i=F+n+n+1;i<=F+n+n+D;i++)
add(i,t);
for(int i=1;i<=n;i++)
{
int fcnt(read()),dcnt(read());
add(F+i,F+n+i);
while(fcnt--)
{
int x(read());
add(x,F+i);
}
while(dcnt--)
{
int x(read());
add(F+n+i,F+n+n+x);
}
}
int ans=0;
while(bfs()==true)
ans+=dfs(s,2e18);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号