题意:
有N个城市,M条单向路,Tom想环游全部城市,每次至少环游2个城市,每个城市只能被环游一次。由于每条单向路都有长度,要求游遍全部城市的最小长度。
// 给定一个有向图,必须用若干个环来覆盖整个图,要求这些覆盖的环的权值最小。
思路:
原图每个点 u拆为 u和 u',从源点引容量为 1费用为 0的边到 u,从 u'引相同性质的边到汇点,若原图中存在 (u, v),则从 u 引容量为 1费用为 c(u, v)的边到 v'
这里源模拟的是出度,汇模拟的是入度,又每个点的出度等于入度等于 1,那么如果最大流不等于顶点数 n,则无解;否则,答案就是最小费用。
#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=500 ;
const int M=10000 ;
const int inf=1<<30 ;
struct node
{
int u,v,c,cost,next;
}edge[M<<2];
int pre[N],dist[N],vist[N],head[N];
int top,maxflow ;
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(vist,0,sizeof(vist));
memset(pre,-1,sizeof(pre));
int u ,v ;
queue<int>q;
for(int i = 0 ; i < n ; i++) dist[i]=inf ;
vist[s]=1;dist[s]=0;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vist[u]=0;
for(int 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 flow=0,mincost=0,minflow;
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 ;
}
flow += minflow ;
mincost += dist[t]*minflow ;
}
maxflow=flow ;
return mincost ;
}
int main()
{
int n,m,u,v,c;
while(~scanf("%d%d",&n,&m))
{
top=0;
memset(head,-1,sizeof(head));
int 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);
if(maxflow != n) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
另一种做法 : 最小权匹配
#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=500 ;
const int inf=1<<30;
int match[N],lx[N],ly[N],fx[N],fy[N];
int g[N][N];
int n,m;
int dfs(int u)
{
fx[u]=1;
for(int i = 1 ; i <= n ; i++)
{
if(!fy[i] && lx[u]+ly[i]==g[u][i])
{
fy[i]=1;
if(match[i]==-1 || dfs(match[i]))
{
match[i]=u;
return 1;
}
}
}
return 0;
}
int KM_match()
{
int a ;
for(int i = 1 ; i <= n ; i++)
{
lx[i]=-inf;
for(int j = 1 ; j <= n ; j++)
lx[i]=max(lx[i],g[i][j]);
}
memset(match,-1,sizeof(match));
memset(ly,0,sizeof(ly));
for(int k = 1 ; k <= n ; k++)
{
while(1)
{
memset(fx,0,sizeof(fx));
memset(fy,0,sizeof(fy));
if(dfs(k)) break ;
a=inf ;
for(int i = 1 ; i <= n ; i++)
if(fx[i])
for(int j = 1 ; j <= n ; j++)
if(!fy[j])
a=min(a,lx[i]+ly[j]-g[i][j]);
for(int i = 1 ; i <= n ; i++)
{
if(fx[i]) lx[i] -= a ;
if(fy[i]) ly[i] += a ;
}
}
}
int sum=0;
for(int i = 1 ; i <= n ;i++)
{
if(match[i]==-1 || g[match[i]][i]==-inf) { sum=-1 ; return sum ;}
sum += g[match[i]][i] ;
}
return -sum ;
}
int main()
{
int a,b,w;
while(~scanf("%d%d",&n,&m))
{
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
g[i][j]=-inf ;
while(m--)
{
scanf("%d%d%d",&a,&b,&w);
if(g[a][b] <-w)
g[a][b]=-w;
}
int ans=KM_match();
printf("%d\n",ans);
}
return 0;
}