题意:有N个城市M单程公路将它们连接起来;要求路路应该包含一个或多个循环
a->B->…->p->A.每个城市都应该走一条路,在一条线路上,每个城市只需要访问一次
,也就是一个城市只能被一条线路覆盖
(题目已经说明第一个城市和最后一个城市除外)
思路:采用最大权完美匹配算法,但是最大完美匹配算法求的是最大权值,完美只要把所有的权值变为负值就可以了。将一个点拆成两个点求最大权完美匹配:
最大权完美匹配:二分图最大匹配是寻找最大匹配数,用匈牙利算法。当连 接的边带有权值时,要寻找匹配后权值和最大的方案,且保证 A 集合中的点均有 B 中的点能匹配。此时问题就转化为二分图最大权完美匹配。
KM 算法核心为: 为每一点添加顶标, 在顶标的限制下用匈牙利算法处理出最大匹配数, 若最大匹配数 =n, 则达到最优解, 输出。否则修改
顶标, 再用匈牙利算法处理, 如此重复。
设二分图的两部分点集分别为 X={X1,X2,…,Xn}X={X1,X2,…,Xn} 和 Y={Y1,Y2,…,Ym}Y={Y1,Y2,…,Ym}, ⟨Xi,Yj⟩⟨Xi,Yj⟩
的边权为 wij
给两部分点集分别赋点权 {Ai},{Bi}{Ai},{Bi},
使得 Ai+Bj⩾wij,
取等的边的生成子图叫做相等子图。那么相等子图的完美匹配就是最大权匹配。我们需要适当选取权值,使相等子图有完美匹配。
算法步骤:
1.若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
2.若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。
操作为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全
部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d
注意:有重边
模版一:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<stack>
using namespace std;
const int maxx=1005;
const int inf=0x3f3f3f3f;
int w[maxx][maxx];
int lx[maxx],ly[maxx];
int visx[maxx],visy[maxx];
int slack[maxx];
int nxt[maxx];
int linker[maxx];
int n,m;
int Find(int x){
visx[x]=1;
for(int y=1;y<=n;y++){
if(visy[y]==0){
int temp=lx[x]+ly[y]-w[x][y];
if(temp==0){
visy[y]=1;
if(linker[y]==-1||Find(linker[y])){
linker[y]=x;
return 1;
}
}else{
slack[y]=min(slack[y],temp);
}
}
}
return 0;
}
int KM(int n){
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++)lx[i]=-inf;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(lx[i]<w[i][j]){
lx[i]=w[i][j];
}
}
}
for(int k=1;k<=n;k++){
memset(slack,inf,sizeof(slack));
while(true){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(Find(k)){
break;
}
int d=inf;
for(int i=1;i<=n;i++){
if(visy[i]==0){
d=min(d,slack[i]);
}
}
for(int i=1;i<=n;i++){
if(visx[i]==1){
lx[i]-=d;
}
}
for(int i=1;i<=n;i++){
if(visy[i]){
ly[i]+=d;
}else{
slack[i]-=d;
}
}
}
}
int res=0;
for(int i=1;i<=n;i++){
if(linker[i]!=-1){
res+=-w[linker[i]][i];
}
}
return res;
}
void init(){
memset(linker,-1,sizeof(linker));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
w[i][j]=-inf;
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=m;i++){
int a,b,cost;
scanf("%d %d %d",&a,&b,&cost);
w[a][b]=max(-cost,w[a][b]);
}
int ans=KM(n);
cout<<ans<<endl;
}
return 0;
}
模版二:
#include<iostream>
#include<algorithm>
#include<stack>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
const int maxx=500;
const int inf=0x3f3f3f3f;
int n,m,w[maxx][maxx];
int mb[maxx],vb[maxx],ka[maxx],kb[maxx],p[maxx],c[maxx];
int qf,qb,q[maxx];
void Bfs(int u){
int a,v=0,vl=0,d;
for(int i=1;i<=n;i++) {
p[i]=0,c[i]=inf;
}
mb[v]=u;
do {
a=mb[v],d=inf,vb[v]=1;
for(int b=1;b<=n;b++)if(!vb[b]){
if(c[b]>ka[a]+kb[b]-w[a][b]){
c[b]=ka[a]+kb[b]-w[a][b];
p[b]=v;
}
if(c[b]<d) {
d=c[b];
vl=b;
}
}
for(int b=0;b<=n;b++){
if(vb[b]) {
ka[mb[b]]-=d;
kb[b]+=d;
}else {
c[b]-=d;
}
}
v=vl;
} while(mb[v]);
while(v) {
mb[v]=mb[p[v]];
v=p[v];
}
}
void init(){
memset(ka,0,sizeof(ka));
memset(kb,0,sizeof(kb));
memset(mb,0,sizeof(mb));
memset(p,0,sizeof(p));
memset(vb,0,sizeof(vb));
memset(c,0,sizeof(c));
memset(q,0,sizeof(q));
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++){
w[a][b]=-inf;
}
}
}
int KM(){
for(int i=1;i<=n;i++) {
mb[i]=ka[i]=kb[i]=0;
}
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++) {
vb[b]=0;
}
Bfs(a);
}
int res=0;
for(int b=1;b<=n;b++) {
res+=-w[mb[b]][b];
}
return res;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
init();
for(int i=1;i<=m;i++){
int u,v,cost;
scanf("%d %d %d",&u,&v,&cost);
w[u][v]=max(w[u][v],-cost);
}
int ans=KM();
cout<<ans<<endl;
}
return 0;
}
对于模版二,我自己也是还在学习中!