题目链接

其实这道题和某道P1231 教辅的组成几乎是双倍经验,考点也一样

分析

这道题是一道最大匹配问题,结合数据范围可以很容易的想到网络流

我们发现酒店的房间和菜都是和人有关的,所以我们把人和房间、菜分别连边,并且从原点s向
房间连边,菜向汇点连边

但是每一个顾客的贡献只能计算一次,
也就是只能和一道菜和一个房间匹配。

如果像上面那样连边会造成错误,如:

X点的贡献变成了2,实际应是1

所以我们考虑将代表人的点拆开,分为两组,
一组为入点,一组为出点

房间向入点连边,出点向菜连边,入点和出点之间连边

所有的边流量为1,代表只能使用一次
即:

\(\begin{aligned}&S\xrightarrow{1}P_{room}\\&P_{meal}\xrightarrow{1}T \\&P_{room}\xrightarrow{1}P_{people}.in\\&P_{people}.out\xrightarrow{1}P_{meal}\\&P_{people}.in\xrightarrow{1}P_{people}.out\end{aligned}\)

代码

\(\mathcal{Code:}\)

#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
#define int long long
#define debug cout<<__LINE__<<" "<<__FUNCTION__<<"\n"
inline int read(){
    int x=0,y=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*y;
}
int n_1,n_2,n_3,m_1,m_2,s,t,dep[N];
int head[N],used[N],tot=1,front;//tot赋1方便找反向边
int ans;
struct Node{
    int nxt,to,dis;
}edge[N<<1];
inline void add(int x,int y,int z){
    edge[++tot].nxt=head[x];
    edge[tot].to=y;
    edge[tot].dis=z;
    head[x]=tot;
}
queue<int> q;
inline int bfs(){
    register int i;
    for(i=0;i<=n_1+n_1+n_2+n_3+2;i++) dep[i]=-1,used[i]=head[i];
    //记得在初始化时一定算好大小点的数量
    dep[s]=0;
    q.push(s);
    while(!q.empty()){
        front=q.front();q.pop();
        for(i=head[front];i;i=edge[i].nxt){
            if(edge[i].dis&&dep[edge[i].to]==-1){
                dep[edge[i].to]=dep[front]+1;q.push(edge[i].to);
            }
        }
    }
    return dep[t]!=-1;
}
int dfs(int now,int limit){
    if(!limit||now==t) return limit;
    int flow=0;
    for(int &i=used[now],pro;i;i=edge[i].nxt){
        if(dep[edge[i].to]==dep[now]+1&&(pro=dfs(edge[i].to,min(limit,edge[i].dis)))){
            edge[i].dis-=pro;
            edge[i^1].dis+=pro;
            flow+=pro;
            limit-=pro;
        }
    }
    return flow;
}
inline void Dinic(){//最大流板子
    while(bfs()) ans+=dfs(s,1000000001LL);
}
signed main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    n_1=read();n_2=read();n_3=read();//people room meal
    s=0;t=n_1+n_1+n_2+n_3+1;//S和T的取值(个人习惯)
    register int i,j,u;
    for(i=n_1+n_1+1;i<=n_1+n_1+n_2;i++){//从S到P_room
        add(s,i,1);add(i,s,0);
    }
    for(i=n_1+n_1+n_2+1;i<=n_1+n_1+n_2+n_3;i++){//从P_meal到T
        add(i,t,1);add(t,i,0);
    }
    for(i=1;i<=n_1;i++){//从P_people.in到P_people.out
        add(i,i+n_1,1);add(i+n_1,i,0);
    }
    for(i=1;i<=n_1;i++){//从P_room到P_people.in
        for(j=1;j<=n_2;j++){
            u=read();
            if(u){
                add(j+n_1+n_1,i,1);add(i,j+n_1+n_1,0);
            }
        }
    }
    for(i=1;i<=n_1;i++){//从P_people.out到P_meal
        for(j=1;j<=n_3;j++){
            u=read();
            if(u){
                add(i+n_1,j+n_1+n_1+n_2,1);add(j+n_1+n_1+n_2,i+n_1,0);
            }
        }
    }
    Dinic();
    cout<<ans<<"\n";
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}
01-19 14:43