3739. 【TJOI2014】匹配(match)
作者:互联网
Description
有 \(N\) 个单身的男孩和 \(N\) 个单身女孩,男孩 \(i\) 和女孩 \(j\) 在一起得到的幸福值为 \(H_{i,j}\) 。一个匹配即对这 \(N\) 个男孩女孩的安排:每个男孩恰好有一个女朋友, 每个女孩恰好有一个男朋友。
一个匹配的幸福值即这 \(N\) 对男女朋友的幸福值的和。经典的问题是计算幸福值最大的匹配,即完美匹配。然而完美匹配有时候并不唯一,你需要计算,对于所有的完美匹配,其交集是什么。
Solution
这题是经典的费用流/带权网络流,我写的是费用流。
先用最大费用最大流的模板求出完美匹配。
题目要求的交集就是指那些一定在完美匹配中的边,一种朴素的想法是枚举每条男孩女孩间的边,将它删去,然后再跑最大费用最大流,看两次的结果有无变化,若变小则说明删去的那条边是一定在残量网络中的。
但这种做法会超时,考虑优化,注意到在第一次跑完费用流后,此时每个男孩有且仅有一条边连向女孩,而其他的所有边可以确定不在交集中,因为已经至少有一种方法不走它们了。
那么我们把这些边找出来,将每条边一一删去,然后再跑费用流,可以将时间复杂度下降一个数量级。
Code
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100
#define inf 2147483647
using namespace std;
struct node
{
int to,next,flow,val,head,flag;
}a[N*N*4];
deque<int> q;
int n,x,S,T,tot=1,ans,sum,dis[N<<1],c[N],cur[N<<1];
bool bj[N<<1],bz[N<<1],bb[N*N*4];
void add(int x,int y,int fl,int va)
{
a[++tot].to=y;
a[tot].flow=fl;
a[tot].val=va;
a[tot].next=a[x].head;
a[x].head=tot;
}
bool check()
{
for (int i=1;i<=2*n+2;++i)
dis[i]=-inf,bj[i]=false;
q.push_back(T);
dis[T]=0;
while (!q.empty())
{
int x=q.front();q.pop_front();
for (int i=a[x].head;i;i=a[i].next)
{
if (bb[i]) continue;
int y=a[i].to;
if (a[i].flag) continue;
if (a[i^1].flow&&dis[y]<dis[x]-a[i].val)
{
dis[y]=dis[x]-a[i].val;
if (!bj[y])
{
bj[y]=true;
if (q.empty()||dis[y]<dis[q.front()]) q.push_back(y);
else q.push_front(y);
}
}
}
bj[x]=false;
}
return dis[S]>-inf;
}
int calc(int now,int flow)
{
if (now==T) return flow;
bz[now]=true;
int fw=0;
for (int i=cur[now];i;i=a[i].next)
{
if (bb[i]) continue;
int v=a[i].to;
if (!bz[v]&&dis[now]==dis[v]+a[i].val&&a[i].flow)
{
int f=calc(v,min(flow,a[i].flow));
if (f)
{
a[i].flow-=f;
a[i^1].flow+=f;
fw+=f;
flow-=f;
cur[now]=i;
if (!flow) break;
}
}
}
return fw;
}
int fyl()
{
int res=0;
while (check())
{
bz[T]=true;
while (bz[T])
{
for (int i=1;i<=2*n+2;++i)
cur[i]=a[i].head,bz[i]=false;
res+=dis[S]*calc(S,inf);
}
}
return res;
}
void beback()
{
for (int i=a[S].head;i;i=a[i].next)
a[i].flow=1;
for (int i=1;i<=n;++i)
{
for (int j=a[i].head;j;j=a[j].next)
{
if (a[j].to==S) a[j].flow=0;
if (a[j].to>n&&a[j].to<=2*n) a[j].flow=1;
}
}
for (int i=n+1;i<=2*n;++i)
{
for (int j=a[i].head;j;j=a[j].next)
{
if (a[j].to==T) a[j].flow=1;
if (a[j].to<=n) a[j].flow=0;
}
}
for (int i=a[T].head;i;i=a[i].next)
a[i].flow=0;
}
int main()
{
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
{
scanf("%d",&x);
add(i,j+n,1,x);
add(j+n,i,0,-x);
}
S=n*2+1;T=n*2+2;
for (int i=1;i<=n;++i)
{
add(S,i,1,0);add(i,S,0,0);
add(i+n,T,1,0);add(T,i+n,0,0);
}
ans=fyl();
printf("%d\n",ans);
sum=ans;
for (int i=1;i<=n;++i)
{
for (int j=a[i].head;j;j=a[j].next)
{
if (a[j].to>n&&a[j].to<=2*n)
{
if (!a[j].flow)
{
c[i]=j;
break;
}
}
}
}
for (int i=1;i<=n;++i)
{
beback();
bb[c[i]]=true;
bb[c[i]^1]=true;
ans=fyl();
if (ans<sum) printf("%d %d\n",i,a[c[i]].to-n);
bb[c[i]]=false;
bb[c[i]^1]=false;
}
return 0;
}
标签:include,匹配,int,flow,男孩,3739,TJOI2014,now,match 来源: https://www.cnblogs.com/Livingston/p/15803429.html