用途:求出带权二分图的最大权匹配
流程:
1. 把左边每个点的顶标设为与它相连的边中最大的权值,右边每个点顶标设为(0)。
2. 对于每个点(i),进行步骤(3)。
3. 在与(i)相连的所有边中找到一条边((i,u))使得(w(i,u)=val(i)+val(u)),如果(u)未匹配则匹配(i)和(u),进入步骤(4);如果(u)已匹配则对(u)重复步骤(3)。都找完后进入步骤(4)。
4. 若找到了满足条件的点(u),则匹配(i)和(u),重复步骤(2)。若没有找到,进入步骤(5)。
5. 把上次的(3)操作中涉及的所有左边点顶标(-1),右边点顶标(+1),重复步骤(3)。
可以记录最少的顶标差d,将其优化至(n^3)
举个例子:
懒得举了
代码如下:
此为n^4代码
#include<bits/stdc++.h>
using namespace std;
int n,m,e;
int array[1001][1001];//邻接存图
int match[1001];//匹配
int value_left[1001];//左点顶标
int value_right[1001];//右点顶标
int visit_left[1001];//左点标记
int visit_right[1001];//右点标记
int ans;
inline int read(){//快读
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int augment(int x){//增广操作(步骤3)
visit_left[x]=1;//给涉及的点打标记
for(int i=1;i<=m;i++){//遍历每一条边
if(!visit_right[i]&&value_right[i]+value_left[x]==array[x][i]){//如果i此次未被涉及过且满足条件
visit_right[i]=1;//涉及i
if(match[i]==-1||augment(match[i])){//如果i未被匹配
match[i]=x;//把i与x匹配
return 1;//返回成功
}
}
}
return 0;//如果一个都不行,返回失败
}
int main(){
memset(match,-1,sizeof match);
n=read();
m=read();
e=read();
for(int i=1;i<=e;i++){
int u,v,w;
u=read();
v=read();
w=read();
array[u][v]=w;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){//处理左点顶标
value_left[i]=max(value_left[i],array[i][j]);
}
}
for(int i=1;i<=n;i++){//每次记得清空标记
memset(visit_left,0,sizeof visit_left);
memset(visit_right,0,sizeof visit_right);
while(!augment(i)){//对于一个点如果增广不成功就不停重复
for(int j=1;j<=n;j++){//如果左点被访问则-1
value_left[j]-=visit_left[j];
}
for(int j=1;j<=m;j++){//如果右点被访问则+1
value_right[j]+=visit_right[j];
}
memset(visit_left,0,sizeof visit_left);
memset(visit_right,0,sizeof visit_right);
}
}
for(int i=1;i<=m;i++){//统计匹配的答案
if(match[i]!=-1)ans+=array[match[i]][i];
}
cout<<ans;
return 0;
}