P2756 飞行员配对方案问题

传送门

考虑网络流:

源点分别向1m连一条流量为1的边,表示每个外籍飞行员最多有一个贡献。

m+1n都向汇点连一条流量为1的边,表示每个英国飞行员最多一个贡献

uv连一条流量为1的边,表示u号外籍飞行员和v号英国飞行员可以成为搭档

网络流算法求最大流即可

考虑怎么找方案:

思考Dinic计算答案的方式,可以发现每一个外籍飞行员连向的一定的是英国飞行员,dfs时记下pre,到达汇点时求一下方案即可

理解:反向边对于我们来说是一个反悔操作,然后我们若手动模拟一下Dinic算法会发现:当我们走反向边时,我们一定是从英国飞行员走到外籍飞行员。什么意思呢?就是原来的这对搭档要拆开,英国飞行员要与正向边连向他的人合作,外籍飞行员要与他连向的人合作。因为外籍飞行员没有连向汇点,所以外籍飞行员一定会连向英国飞行员,他们做搭档即可(细节自己看代码)。

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=100000,INF=1e15;
ll m,n,u,v;
struct jgt
{
ll to;
ll w;
ll ne;
}e[N];
ll h[N],idx=1;
ll st,en;
void add(ll a,ll b,ll c)
{
e[++idx].to=b;
e[idx].w=c;
e[idx].ne=h[a];
h[a]=idx;
e[++idx].to=a;
e[idx].w=0;
e[idx].ne=h[b];
h[b]=idx;
}
ll dis[N];
ll now[N],ans;
ll pre[N];
ll le[N],ri;
bool bfs()
{
for(ll i=st;i<=en;i++) dis[i]=INF;
queue<ll> q;
q.push(st);
dis[st]=0;
now[st]=h[st];
while(!q.empty())
{
ll wz=q.front();
q.pop();
for(ll i=h[wz];i;i=e[i].ne)
{
ll j=e[i].to;
if(e[i].w&&dis[j]==INF)
{
dis[j]=dis[wz]+1;
now[j]=h[j];
q.push(j);
if(j==en) return true;
}
}
}
return false;
}
void xg()
{
for(ll i=en;i!=st;i=pre[i])
{
if(i>=m+1&&i<=m+n) ri=i;
if(i>=1&&i<=m) le[i]=ri;
}
}
ll dfs(ll wz,ll sum)
{
if(wz==en)
{
xg();
return sum;
}
ll re=0,k;
for(ll i=now[wz];i&&sum;i=e[i].ne)
{
now[wz]=i;
ll j=e[i].to;
if(e[i].w&&dis[j]==dis[wz]+1)
{
pre[j]=wz;
k=dfs(j,min(sum,e[i].w));
re+=k;
sum-=k;
e[i].w-=k;
e[i^1].w+=k;
}
}
return re;
}
int main()
{
scanf("%lld %lld",&m,&n);
st=0;
en=n+1;
for(ll i=1;i<=m;i++)
add(st,i,1);
for(ll i=m+1;i<=n;i++)
add(i,en,1);
while(1)
{
scanf("%lld %lld",&u,&v);
if(u==-1&&v==-1) break;
add(u,v,1);
}
while(bfs()) ans+=dfs(st,INF);
printf("%lld\n",ans);
for(ll i=1;i<=m;i++)
if(le[i]!=0) printf("%lld %lld\n",i,le[i]);
return 0;
}
posted @   傻阙的缺  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示