这三道题都基本差不多:给定一个图,让每一个点都在一个环中,并且使得这些环的权值之和最小。
这类型的题目有至少两种做法:最小费用流或者最大完美匹配。但是具体选择哪一种要结合具体的数据量范围。最大完美匹配的时间复杂度是O(n^3),最小费用流的时间复杂度是O(F*E*log*V),F是做最短路的次数。
最小费用流的做法:建图方式是,将一个点拆成两个,编号为i,i+n,对于一条边(u,v,val),添加一条边u->v+n,容量为inf,边权为val。然后,原点(0)向每一个点都建立一条边0->i,容量为1,权值为0。最后,每一个点i向汇点end建立一条边i->end,容量为1,边权为0。然后,求一次s到end的流量为n的最小费用流就行了。这样成立的原因是,在这个图中,每一个点都只会从s流入一个流量,向汇点流出一个流量,这个和一个环的情况是一样的,并且保证了最后边权之和是最小的。
最大完美匹配的做法:将一个点拆成两个之后建立一个二分图,一条(u,v,val)的边,就可以左边的u向右边的v建立一条权值为val的边。然后做一次最大完美匹配就行了。成立的原因是,如果有完美匹配,那么一个点在左边的点和在右边的点都被覆盖了,和在一个环中的情况一样。
HDU3435的代码(最小费用流)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
#include<map>
#include<queue>
#include<climits>
#include<assert.h>
#include<functional>
using namespace std;
const int maxn=2005;
const int INF=INT_MAX/2;
typedef pair<int,int> P;
struct edge
{
int to,cap,cost,rev;
edge(int t,int c,int co,int r)
:to(t),cap(c),cost(co),rev(r){}
edge(){}
};
int V;//the number of points
vector<edge>G[maxn];
int h[maxn];
int dist[maxn];
int prevv[maxn],preve[maxn];
void add_edge(int from,int to,int cap,int cost)
{
G[from].push_back(edge(to,cap,cost,G[to].size()));
G[to].push_back(edge(from,0,-cost,G[from].size()-1));
}
void clear()
{
for(int i=0;i<V;i++) G[i].clear();
}
int min_cost_flow(int s,int t,int f)
{
int res=0;
fill(h,h+V,0);//如果下标从1开始,就要+1
while(f>0)
{
priority_queue<P,vector<P>,greater<P> >que;
fill(dist,dist+V,INF);
dist[s]=0;
que.push(P(0,s));
while(!que.empty())
{
P cur=que.top();que.pop();
int v=cur.second;
if(dist[v]<cur.first) continue;
for(int i=0;i<G[v].size();i++)
{
edge &e=G[v][i];
if(e.cap>0&&dist[e.to]>dist[v]+e.cost+h[v]-h[e.to])
{
dist[e.to]=dist[v]+e.cost+h[v]-h[e.to];
prevv[e.to]=v;
preve[e.to]=i;
que.push(P(dist[e.to],e.to));
}
}
}
if(dist[t]==INF)
{
return -1;
}
for(int v=0;v<V;v++) h[v]+=dist[v];//从0还是1开始需要结合题目下标从什么开始
int d=f;
for(int v=t;v!=s;v=prevv[v])
{
d=min(d,G[prevv[v]][preve[v]].cap);
}
f-=d;
res+=d*h[t];
for(int v=t;v!=s;v=prevv[v])
{
edge &e=G[prevv[v]][preve[v]];
e.cap-=d;
G[v][e.rev].cap+=d;
}
}
return res;
}
int main()
{
int T;
scanf("%d",&T);
for(int Case=1;Case<=T;Case++)
{
int n,m;
scanf("%d%d",&n,&m);
clear();
V=2*n+2;
int a,b,_v;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&_v);
add_edge(a,b+n,INF,_v);
add_edge(b,a+n,INF,_v);
}
for(int i=1;i<=n;i++)
{
add_edge(0,i,1,0);
add_edge(i+n,2*n+1,1,0);
}
int mincost=min_cost_flow(0,2*n+1,n);
printf("Case %d: ",Case);
if(mincost==-1) printf("NO\n");
else printf("%d\n",mincost);
}
}