题意:有N个城市,M条街道(连接两点的距离),每条街道是单向的,现在要你设计多条路线覆盖所有的点,每条路线都是一个环,并且每个点仅能被一条路线覆盖且只经过一次(终始点除外),
分析:因为是有向圈,所以每个点的入度和出度应该都是1,故将一个点拆成两个点,入度点和出度点,然后用最佳匹配即可!(因为最佳匹配是求最大值,故我们把边权设为负值即可!)
注意:这题有重边,题目太不道德了,有重边都不说,还要猜的啊!有些题没说有重边就没重边,有些题没说有重边但是它就是有重边!无敌了都!
KM_match :
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int inf=10000000;
const int N=205;
int m[N][N]; //邻接矩阵
int lx[N],ly[N]; //顶点标号
int fx[N],fy[N];//是否已经搜索过,S为寻找从i出发的增广轨时访问的x中的点的集合,T为访问的y中的点的集合。
int match[N];
int n;
int dfs(int k) //从x[k]寻找增广路
{
fx[k]=1;
for(int i=1; i<=n; i++)
{
if(!fy[i]&& lx[k]+ly[i]==m[k][i])//相等子图
{
fy[i]=1;
if(match[i]==-1||dfs(match[i]))
{
match[i]=k;
return 1;
}
}
}
return 0;
}
int KM_match() //求解最小权匹配
{
int Min;
for(int i=1; i<=n; i++)
{
lx[i]=-1;//x中顶点i的编号为与i关联的Y中边的最大权重
for(int j=1; j<=n; j++)
if(lx[i]<m[i][j])
lx[i]=m[i][j];//初始化x的顶点标号
}
memset(ly,0,sizeof(ly));//初始化y的顶点标号
memset(match,-1,sizeof(match));
for(int k=1; k<=n; k++)
{
while(1)
{
memset(fx,0,sizeof(fx));
memset(fy,0,sizeof(fy));
if(dfs(k)) break;
Min=inf;
for(int i=1; i<=n; i++)
if(fx[i]) //x在交错树中
for(int j=1; j<=n; j++)
if(!fy[j])//y不在交错树中,扩大子图
Min=min(Min,lx[i]+ly[j]-m[i][j]);
//若匹配不成功,则修改顶点标号,找到d的值
for(int i = 1 ; i <= n ;i++) if(fx[i]) lx[i] -= Min ;
for(int i = 1 ; i <= n ;i++) if(fy[i]) ly[i] += Min ;
}
}
int sum=0;
for(int i=1; i<=n; i++)
sum+=m[match[i]][i];
return -sum;
}
int main()
{
int t;
cin>>t;
while(t--)
{
int M,ans;
cin>>n>>M;
for(int i=0; i<=n; i++)
for(int j=0; j<=n; j++)
m[i][j]=-inf;//即将边的权值取反
while(M--)
{
int u,v,w;
cin>>u>>v>>w;
if(m[u][v]<-w)//WA的地方,注意呀!!!
m[u][v]=-w;
}
ans=KM_match();
printf("%d\n",ans);
}
return 0;
}
最小费用最大流 :
也是拆点, (s,i,1,0) ,(i+n,t,1,0) ,(a,b+n,1,cost) ;
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=2500 ;
const int inf=1<<30;
struct node
{
int u,v,c,cost,next;
} edge[N*100] ;
int dist[N],pre[N],head[N],vist[N];
int g[N][N],top;
void add(int u ,int v ,int c,int cost)
{
edge[top].u=u;edge[top].v=v;edge[top].c=c;edge[top].cost=cost;edge[top].next=head[u];head[u]=top++;
edge[top].u=v;edge[top].v=u;edge[top].c=0;edge[top].cost=-cost;edge[top].next=head[v];head[v]=top++;
}
int SPFA(int s,int t,int n)
{
memset(pre,-1,sizeof(pre));
memset(vist,0,sizeof(0));
for(int i =0 ; i <= n ;i++) dist[i]=inf ;
int i,u,v;
vist[s]=1;dist[s]=0;
queue<int>q ;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vist[u]=0;
for( i = head[u] ; i!=-1 ; i=edge[i].next)
{
v=edge[i].v ;
if(edge[i].c && dist[v] > dist[u]+edge[i].cost)
{
dist[v] = dist[u]+edge[i].cost ;
pre[v]=i;
if(!vist[v])
{
vist[v]=1;
q.push(v);
}
}
}
}
if(dist[t]==inf) return 0;
return 1;
}
int MFMC(int s,int t,int n)
{
int minflow,mincost=0,flow=0;
while(SPFA(s,t,n))
{
minflow=inf ;
for(int i=pre[t];i!=-1;i=pre[edge[i].u])
minflow=min(minflow,edge[i].c) ;
for(int i=pre[t];i!=-1;i=pre[edge[i].u])
{
edge[i].c -= minflow;
edge[i^1].c += minflow ;
}
mincost += dist[t]*minflow ;
flow+=minflow ;
}
return mincost ;
}
int main()
{
int T,n,m,s,t,u,v,c;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
top=0;
memset(head,-1,sizeof(head));
s=0,t=2*n+1 ;
while(m--)
{
scanf("%d%d%d",&u,&v,&c);
add(u,v+n,1,c);
}
for(int i = 1 ; i <= n ; i++)
add(s,i,1,0),add(i+n,t,1,0);
int ans=MFMC(s,t,t+1);
printf("%d\n",ans);
}
return 0;
}
本文介绍了解决有向图中最佳匹配问题的方法,通过拆点转化为无向图并使用Kuhn-Munkres算法求解;同时探讨了最小费用最大流问题,并给出了详细的最小费用最大流算法实现过程。
322

被折叠的 条评论
为什么被折叠?



