芝士:匈牙利&KM
背景
解决二分图最大匹配,以及完美匹配的最大权值
匈牙利
思路
考虑二分图上已经有了一些匹配,
然后现在我们将一个点加进去,如果这个点本身相连的点就有没有已经匹配的,那么就直接匹配上去,
如果所有的点都已经匹配,那么就让这些匹配的点试着改变他们所匹配的点
整体上类似于调整法
代码
#include<iostream>
#include<vector>
using namespace std;
int n,m,e;
vector<int> g[1005];
int mat[1005],vis[1005];//匹配的点
int ans=0;
bool dfs(int u,int fl)
{
if(vis[u]==fl)
return 0;
vis[u]=fl;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(mat[v]==0||dfs(mat[v],fl))
{
mat[v]=u;
return 1;
}
}
return 0;
}
int main()
{
cin>>n>>m>>e;
for(int i=1,u,v;i<=e;i++)
{
cin>>u>>v;
g[u].push_back(v);
}
for(int i=1;i<=n;i++)
if(dfs(i,i))
ans++;
cout<<ans;
return 0;
}
KM
思路
考虑也是用调整法去构造出一个解
我们将点赋上点权,\(x\)部分的点的点权初始为他们所连出去的边的最大的权值,\(y\)部分的点为0,设\(x\)部分的点的点权为\(lx_i\),\(y\)部分的点的点权为\(ly_i\),
显然,\(ans\le \sum_{i=1}^nlx_i+ly_i\),
接着就开始调整,但必须满足不管在什么时候都有\(lx[e[i].u]+ly[e[i].v]\ge e[i].w\)
如果我们将\(lx[e[i].u]+ly[e[i].v]==e[i].w\)的边拿出来,将这些边称作可行边,
那么这些可行边必然后构成一个图,显然这个图也是二分图,我们称其为可行图(实际上是笔者自己的定义),接着我们所需要的做的就是不停的向图上加边,使得这个二分图上匹配的点对数量为\(n\)
下文所提到的\(x\)部分和\(y\)部分均为可行边所构成的二分图上的两个部分
因为要保证没有边被删去,所以我们只能对\(x\)和\(y\)整体进行操作,且进行操作的值互为相反数,比如\(x\)整体减去\(d\),那么\(y\)整体就必须加上\(d\),显然,这样的操作不会使边丢失,同时会使一些边加进来,
那么现在有两个问题:
1.可行图不一定联通,我们对哪一个可行子图进行操作(孤点也算可行子图),
2.操作的\(d\)是多少?
因为操作的顺序并不影响最终的结果,所以我们只选择\(x\)部比\(y\)部多\(1\)个的进行操作,应该很容易就可以证明我们一定存在这样的可行子图,
再者,因为最终的答案\(ans=\sum_{i=1}^{n}lx_i+ly_i\),而我们每一次选择必定是让答案减小,所以我们需要选择减少代价最少的一条边加入图中,
这实际上是变相的调整法,可以说明,这样调整之后的结果一定是最优的
调整的过程用匈牙利实现即可
时间复杂度即为\(O(n^4)\),
我们考虑整个过程就只加了一条边进行,所以只需要对这条边进行更新即可,不需要每一次都暴力跑匈牙利,时间复杂度就优化成为了\(O(n^3)\)
代码
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
using namespace std;
struct node
{
int e;
long long w;
};
int n,m;
int mat[505];//右边的匹配到左边的
vector<node> g[505];
int pre[505];
long long lx[505],ly[505];
long long sla[505];//针对右边
bool vis[505];
void bfs(int st)
{
memset(sla,0x3f,sizeof(sla));
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
int now,la=0,_ind=0;
long long minn;
mat[0]=st;
int tot=0;
while(1)
{
tot++;
now=mat[la];
minn=(1ll<<60);
vis[la]=1;
for(int i=0;i<g[now].size();i++)
{
int v=g[now][i].e;
if(vis[v])
continue;
if(sla[v]>lx[now]+ly[v]-g[now][i].w)
{
sla[v]=lx[now]+ly[v]-g[now][i].w;
pre[v]=la;
}
}
for(int i=1;i<=n;i++)
if(sla[i]<minn&&vis[i]==0)
{
minn=sla[i];
_ind=i;
}
for(int i=0;i<=n;i++)
{
if(vis[i])
{
lx[mat[i]]-=minn;
ly[i]+=minn;
}
else
sla[i]-=minn;
}
la=_ind;
if(mat[la]==-1)
break;
}
while(la)
{
mat[la]=mat[pre[la]];
la=pre[la];
}
}
long long km()
{
memset(mat,-1,sizeof(mat));
for(int i=1;i<=n;i++)
bfs(i);
long long ret=0;
for(int i=1;i<=n;i++)
ret=ret+lx[i]+ly[i];
return ret;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1,u,v,w;i<=m;i++)
{
cin>>u>>v>>w;
g[u].push_back((node){v,w});
//map[u][v]=w;
}
cout<<km()<<'\n';
for(int i=1;i<=n;i++)
cout<<mat[i]<<' ';
cout<<'\n';
return 0;
}