您的位置:首页 > 其它

HIT2739 The Chinese Postman Problem(最小费用最大流)

2016-04-16 17:10 483 查看
题目大概说给一张有向图,要从0点出发返回0点且每条边至少都要走过一次,求走的最短路程。

经典的CPP问题,解法就是加边构造出欧拉回路,一个有向图存在欧拉回路的充分必要条件是基图连通且所有点入度等于出度。

而这题,果断联想到混合图欧拉回路的做法,用最小费用最大流解决:

先只考虑所有边都只走一次,计算出各个点的出度和入度,出度不等于入度的点就需要选择几条边去改变调整它们

对于出度多的就和容量网络的汇点连容量出度-入度费用0的边,入度多的源点就向其同样地连边

对于原图中的所有边<u,v>由u向v连容量INF费用该边长度的边,一单位流量流过该边就会使原本入度多的u点的出度+1,原本出度多的v点的入度+1

然后跑最小费用最大流,如果和源汇相关的边都满流那就有一个解了,而最后的解就是所有边的长度和+最小费用最大流的结果

另外注意判断基图连通。

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define INF (1<<30)
#define MAXN 111
#define MAXM 8888
struct Edge{
int u,v,cap,cost,next;
}edge[MAXM];
int vs,vt,NV,NE,head[MAXN];
void addEdge(int u,int v,int cap,int cost){
edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost;
edge[NE].next=head[u]; head[u]=NE++;
edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost;
edge[NE].next=head[v]; head[v]=NE++;
}
int d[MAXN],pre[MAXN];
bool vis[MAXN];
bool SPFA(){
for(int i=0; i<NV; ++i){
d[i]=INF; vis[i]=0;
}
d[vs]=0; vis[vs]=1;
queue<int> que;
que.push(vs);
while(!que.empty()){
int u=que.front(); que.pop();
for(int i=head[u]; i!=-1; i=edge[i].next){
int v=edge[i].v;
if(edge[i].cap && d[v]>d[u]+edge[i].cost){
d[v]=d[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=1;
que.push(v);
}
}
}
vis[u]=0;
}
return d[vt]!=INF;
}
int MCMF(int &mxflow){
int res=0;
while(SPFA()){
int flow=INF,cost=0;
for(int u=vt; u!=vs; u=edge[pre[u]].u){
flow=min(flow,edge[pre[u]].cap);
}
mxflow-=flow;
for(int u=vt; u!=vs; u=edge[pre[u]].u){
edge[pre[u]].cap-=flow;
edge[pre[u]^1].cap+=flow;
cost+=flow*edge[pre[u]].cost;
}
res+=cost;
}
return res;
}
int par[MAXN];
int Find(int x){
while(x!=par[x]){
par[x]=par[par[x]];
x=par[x];
}
return x;
}
bool Union(int a,int b){
int pa=Find(a),pb=Find(b);
if(pa==pb) return 0;
par[pb]=pa;
return 1;
}
int deg[MAXN];
int main(){
int t,n,m,a,b,c;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
vs=n; vt=vs+1; NV=vt+1; NE=0;
memset(head,-1,sizeof(head));
memset(deg,0,sizeof(deg));
for(int i=0; i<n; ++i) par[i]=i;
int res=0,tot=n,mxflow=0;
for(int i=0; i<m; ++i){
scanf("%d%d%d",&a,&b,&c);
addEdge(a,b,INF,c);
res+=c;
++deg[a]; --deg[b];
if(Union(a,b)) --tot;
}
if(tot!=1){
puts("-1");
continue;
}
for(int i=0; i<n; ++i){
if(deg[i]<0) addEdge(vs,i,-deg[i],0);
else addEdge(i,vt,deg[i],0),mxflow+=deg[i];
}
res+=MCMF(mxflow);
if(mxflow!=0) puts("-1");
else printf("%d\n",res);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: