对于二分图的最大匹配和对于带权二分图的最优匹配,B站上
CSU ACM—ICPC 的大佬们讲的已经很清楚了。
有需求的同学们可以去B站上学习一下。
悄悄说一声,讲课的陈佳妮小姐姐也曾经是位OIer,曾在CTSC2016上夺得银牌(因为在HN,好像没能去成NOI),讲解的方式也令OIers十分舒适。
题目大意:给一张有向图,要求选择任意多个环(可重复,非自环,环可以任选,不用相连),使得每个点都被访问过,而且经过的边权之和最小,求最小值。
乍一看和二分图没有关系,但是仔细想一下,如果给图中每个点拆成两个(一个连接这个点的入点,一个连出点),那么因为要走环,那么最优情况肯定是每个环中有一个点会被访问两次,其他点都是一次,那这不就是赤果果的在拆点图上求最优匹配吗?
假设我们有一张图:1——>2——>3——>1,设拆出来的点i,用来连接入点的是i,连接出点的是i+n(此处n=4),那么我们拆点图长这样:5——>2,6——>3,
7——>1,这样就实际上是对拆出来的六个点进行了一次完全匹配。
那么,我们只要先判断拆点图能不能形成完美匹配,然后再跑一边KM就可以了(KM算法要求二分图存在完美匹配,否则会陷入死循环)。
放代码吧:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
vector<int>b[101];
int n,m;
struct PE
{
int s,t,val;
};
PE E[20001];
int mp[101][101];
bool Function(PE x,PE y)
{
return x.val<y.val;
}
int QWx[101],QWy[101],use[101],pre[101],stx[101];
int visx[101],visy[101],fpre[101];
bool Hungary(int x)
{
int temp=0;
visx[x]=1;
for(int i=0;i<b[x].size();i++)
{
int to=b[x][i];
if(visy[to])
{
continue;
}
temp=QWx[x]+QWy[to]-mp[x][to];
if(temp==0)
{
visy[to]=1;
if(pre[to]==0||Hungary(pre[to]))
{
fpre[x]=to;
pre[to]=x;
return 1;
}
}
else
stx[to]=min(stx[to],temp);
}
return 0;
}
bool PDHungary(int x)
{
for(int i=0;i<b[x].size();i++)
{
int to=b[x][i];
if(visy[to])
continue;
visy[to]=1;
if(pre[to]==0||PDHungary(pre[to]))
{
pre[to]=x;
return 1;
}
}
return 0;
}
void KM()
{
for(int i=1;i<=n;i++)
{
memset(stx,0x3f3f3f3f,sizeof(stx));
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(Hungary(i))
{
break;
}
else
{
int date=0x3f3f3f3f;
for(int j=1;j<=n;j++)
{
if(!visy[j])
date=min(date,stx[j]);
}
for(int j=1;j<=n;j++)
{
if(visx[j])
QWx[j]-=date;
if(visy[j])
QWy[j]+=date;
else
stx[j]-=date;
}
}
}
}
}
int LINYIN()
{
//scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
b[i].clear();
}
int ans=0;
memset(mp,-0x3f3f3f3f,sizeof(mp));
memset(QWx,-0x3f3f3f3f,sizeof(QWx));
memset(QWy,0,sizeof(QWy));
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&E[i].s,&E[i].t,&E[i].val);
E[i].val*=-1;
}
sort(E+1,E+1+m,Function);
mp[E[1].s][E[1].t]=E[1].val;
b[E[1].s].push_back(E[1].t);
for(int i=2;i<=m;i++)
{
if(E[i].s==E[i-1].s&&E[i].t==E[i-1].t&&E[i].val==E[i-1].val)
continue;
mp[E[i].s][E[i].t]=max(mp[E[i].s][E[i].t],E[i].val);
b[E[i].s].push_back(E[i].t);
QWx[E[i].s]=max(QWx[E[i].s],E[i].val);
}
for(int i=1;i<=n;i++)
{
memset(visy,0,sizeof(visy));
if(!PDHungary(i))
{
printf("%d\n",-1);
return 0;
}
}
memset(pre,0,sizeof(pre));
memset(visy,0,sizeof(visy));
KM();
for(int i=1;i<=n;i++)
{
ans+=mp[pre[i]][i];
}
ans*=-1;
printf("%d\n",ans);
return 0;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
LINYIN();
}
return 0;
}
完结撒花!