您的位置:首页 > 其它

最小费最大流,最小权最大匹配,拆点法(最优巴士路线设计 uva 1349)

2016-07-13 23:15 501 查看
紫书网络流上的题目,上面的题目简述与分析挺清楚。

给n各点(n<=100)的有向带权图,找若干个有向圈,每个点恰好属于一个圈。要求权和尽量小。若找到,则输出最小的权值,否则输出‘N’。注意及时(u,v)和(v,u)都存在,它们的权值也不一定相同。

每个点恰好属于一个圈,意味着每个点都有一个唯一的后继。反过来,只要每个点都有唯一的后继,每个点一定恰好属于一个圈。“每个东西恰好有唯一的……”让我们想到了二分图匹配。把每个点i拆成Xi和Yi,原图中的有向边u->v对应二分图中的边Xu->Yv,则题目转化为了这个二分图上的最小权完美匹配问题。

Xi就像每个点的尾巴,Yi就像每个点的头,Xu->Yv就是指u的尾巴可以和v的头相连,最小权完美匹配就是让每个尾巴都能且只能找到一个头,同时满足匹配的代价最小。

拆点法,用最小费最大流求最小权完美匹配。

将所有Xi连到源点,容量为1,费用为0。将所有Yi连到汇点,容量为1,费用为0。连Xu->Yv时,容量为1,费用为权值。跑一个从源点到汇点的最小费用最大流,若最大流为点的个数(此时从源点出发的弧与进入汇点的弧都满载)则存在完美匹配,且此时的花费为最小费用,否则不存在完美匹配。

附上渣代码

#include<stdio.h>
#include<vector>
#include<queue>
#include<algorithm>
#define maxn 150
#define INF 0X3F3F3F3F
#include<string.h>
using namespace std;

struct Edge
{
int from,to,cap,flow,cost;
Edge(int u,int v,int c,int f,int w):from(u),to(v),cap(c),flow(f),cost(w){}
};

struct MCMF
{
int n,m;
vector<Edge>edges;
vector<int>G[maxn];
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];

void init(int n)
{
this->n=n;
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}

void AddEdge(int from,int to,int cap,int cost)
{
edges.push_back(Edge(from,to,cap,0,cost));
edges.push_back(Edge(to,from,0,0,-cost));
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}

bool BellmanFord(int s,int t,int& flow,long long& cost)
{
for(int i=0;i<n;i++) d[i]=INF;
memset(inq,0,sizeof(inq));
d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;

queue<int>Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();Q.pop();
inq[u]=0;
for(unsigned int i=0;i<G[u].size();i++)
{
Edge& e=edges[G[u][i]];
if(e.cap>e.flow&&d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to]){Q.push(e.to);inq[e.to]=1;}
}
}
}
if(d[t]==INF) return false;
flow+=a[t];
cost+=(long long)d[t]*(long long)a[t];
for(int u=t;u!=s;u=edges[p[u]].from)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
}
return true;
}

int MincostMaxflow(int s,int t,long long & cost)
{
int flow=0;cost=0;
while(BellmanFord(s,t,flow,cost));
return flow;
}
};

int main()
{
int n;
while(scanf("%d",&n)&&n)
{
MCMF mcmf;
mcmf.init(2*n+2);
for(int i=1;i<=n;i++)
{
//mcmf.AddEdge(i,i+n,1,0);
mcmf.AddEdge(0,i,1,0);
mcmf.AddEdge(i+n,mcmf.n-1,1,0);
int a,b;
while(scanf("%d",&a)&&a)
{
scanf("%d",&b);
mcmf.AddEdge(i,a+n,1,b);
}
}
long long cost=0;
if(n==mcmf.MincostMaxflow(0,mcmf.n-1,cost)) printf("%I64d\n",cost);
else printf("N\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息