当前位置: 代码迷 >> 综合 >> POJ 3281 Dining (最大流,建图拆点)
  详细解决方案

POJ 3281 Dining (最大流,建图拆点)

热度:13   发布时间:2023-11-22 17:40:25.0

题目地址:http://poj.org/problem?id=3281

题目大意:目前有n只牛,f种食物,d种饮料,每种饮料和食物都只有1份,现在我们知道了每头牛喜欢的食物种类和饮料种类,求在为每头牛只分1份食物和一份饮料的情况下,最多能满足多少头牛的需求。

这题我们可以看出是网络流的最大流问题,不过难点还是出现在了建图上。。。

也许我们能想到,把牛列在食物和饮料之间,从s向食物连边,食物向牛连边,牛向饮料连边,饮料再向t连边,那么每次跑出来的路径就能是每头牛跟它喜欢的食物饮料的最大流了
在这里插入图片描述
但这真的就可以了嘛?并不行(一开始没想到这个,直接wa到裂开 qwq ),例如上面这个图,我们会发现我们能找到的最大流会是2(s-1-1-1-t)(s-2-1-2-t),显然这是不行的,因为这个答案是1,只有一头牛。

那我们怎么防止这种一头牛吃两组套餐的出现呢?这回就要涉及到拆点这种方法了,我们把每头牛拆成两个点,然后这两个点之间只连一条流量为1的边
在这里插入图片描述
这样建图之后,我们会发现每头牛最多也只能有1流量了(S-1-1-1-1-T),就可以避免上面的问题了

那么图建成功了,网络流就能直接上了!

直接上代码:

#include<queue> 
#include<cstring>
#include<iostream>
#define ll long long
#define endl '\n'
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e5+5;
struct qxx{
    int fr,w,y;
}q[N];
int n,m,s,t,ans,n1,n2,n3;
int head[1005],zs=-1;
int dis[1005];
void cun(int u,int v,int w)
{
    q[++zs].fr=head[u];q[zs].y=v; q[zs].w=w;head[u]=zs;
}
void build()
{
    for(int i=1;i<=n1;i++)   //s点连食物 cun(0,i,1),cun(i,0,0);for(int i=n1+n2*2+1;i<=n1+n2*2+n3;i++)   //饮料连t点 cun(i,t,1),cun(t,i,0);for(int i=1;i<=n2;i++){
    cun(n1+i,n1+i+n2,1); cun(n1+i+n2,n1+i,0);   //拆点之后,两点连边 int num1,num2,x;cin>>num1>>num2;for(int j=1;j<=num1;j++){
    cin>>x;cun(x,n1+i,1),cun(n1+i,x,0);   //食物连牛的第一个点 }for(int j=1;j<=num2;j++){
    cin>>x;cun(n1+i+n2,n1+n2*2+x,1),cun(n1+n2*2+x,n1+i+n2,0);   //再用牛的第二个点去连饮料 }}
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));queue<int> q1;q1.push(s); dis[s]=0;while(!q1.empty()){
    int u=q1.front(); q1.pop();for(int i=head[u];i!=-1;i=q[i].fr){
    int v=q[i].y;if(dis[v]==-1&&q[i].w){
    q1.push(v);dis[v]=dis[u]+1;}}}return dis[t]!=-1;
}
int dfs(int u,int f)
{
    if(u==t) return f;int res=f;for(int i=head[u];i!=-1;i=q[i].fr){
    int v=q[i].y;if(dis[v]>dis[u]&&q[i].w>0){
    int d=dfs(v,min(f,q[i].w));q[i].w-=d; q[i^1].w+=d;res-=d;if(!res) break;}}if(!res) dis[u]=-1;return f-res;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin>>n2>>n1>>n3;s=0; t=n1+n2*2+n3+1; memset(head,-1,sizeof(head));build();   //建图 while(bfs())   //跑最大流 ans+=dfs(s,1e9);cout<<ans;return 0;
}